Przeglądaj źródła

成绩导出明细分

yin 1 rok temu
rodzic
commit
913ee155b9
22 zmienionych plików z 848 dodań i 2 usunięć
  1. 181 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/ExcelWriterDynamic.java
  2. 13 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/ValueConvertor.java
  3. 50 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/ValueConvertors.java
  4. 23 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/BigDecimalConvertor.java
  5. 45 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/BooleanConvertor.java
  6. 33 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/DateConvertor.java
  7. 28 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/DoubleConvertor.java
  8. 28 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/FloatConvertor.java
  9. 26 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/IntegerConvertor.java
  10. 33 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/LocalDateTimeConvertor.java
  11. 21 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/LongConvertor.java
  12. 21 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/StringConvertor.java
  13. 9 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/DataMap.java
  14. 51 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelColumn.java
  15. 6 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelConstants.java
  16. 9 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelType.java
  17. 24 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/FieldParam.java
  18. 83 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ObjectParam.java
  19. 21 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/XlsWriter.java
  20. 29 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/XlsxWriter.java
  21. 63 1
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/bean/archivescore/ArchiveStudentVo.java
  22. 51 1
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkStudentServiceImpl.java

+ 181 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/ExcelWriterDynamic.java

@@ -0,0 +1,181 @@
+package com.qmth.teachcloud.common.util.excel;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertors;
+import com.qmth.teachcloud.common.util.excel.model.*;
+
+public abstract class ExcelWriterDynamic {
+
+
+    /**
+     * 初始化带表头的Excel生成工具
+     *
+     * @param type 文件格式,XLSX或XLS
+     * @return
+     */
+    public static ExcelWriterDynamic create(ExcelType type) {
+        if (ExcelType.XLSX.equals(type)) {
+            return new XlsxWriter();
+        } else if (ExcelType.XLS.equals(type)) {
+            return new XlsWriter();
+        } else {
+            throw new IllegalArgumentException("Excel type is required");
+        }
+    }
+
+    /**
+     * 创建sheet并写入String[]内容
+     *
+     * @param sheetName    sheet名称
+     * @param titles       前置的标题,可以为空
+     * @param columnNames  数据列名称
+     * @param dataIterator String[]迭代器
+     */
+    public void writeDataArrays(String sheetName, String[] titles, String[] columnNames,
+                                Iterator<String[]> dataIterator) {
+        Sheet sheet = getWorkbook().createSheet(sheetName);
+        AtomicInteger rowIndex = new AtomicInteger(0);
+        //生成标题
+        writeTitle(sheet, rowIndex, titles, columnNames.length);
+        //生成表头
+        writeColumnName(sheet, rowIndex, columnNames);
+        //生成数据
+        while (dataIterator.hasNext()) {
+            writeArray(sheet, rowIndex, columnNames, dataIterator.next());
+        }
+    }
+
+    /**
+     * 创建sheet并写入DataMap对象
+     *
+     * @param sheetName    sheet名称
+     * @param titles       前置的标题,可以为空
+     * @param columnNames  数据列名称
+     * @param dataIterator DataMap对象迭代器
+     */
+    public void writeDataMaps(String sheetName, String[] titles, String[] columnNames, Iterator<DataMap> dataIterator) {
+        Sheet sheet = getWorkbook().createSheet(sheetName);
+        AtomicInteger rowIndex = new AtomicInteger(0);
+        //生成标题
+        writeTitle(sheet, rowIndex, titles, columnNames.length);
+        //生成表头
+        writeColumnName(sheet, rowIndex, columnNames);
+        //生成数据
+        while (dataIterator.hasNext()) {
+            writeData(sheet, rowIndex, columnNames, dataIterator.next());
+        }
+    }
+
+    /**
+     * 创建sheet并写入自定义对象
+     *
+     * @param sheetName      sheet名称
+     * @param titles         前置标题,可以为空
+     * @param objectType     自定义对象类型
+     * @param objectIterator 自定义对象迭代器
+     * @param <E>
+     */
+    public <E> void writeObjects(String sheetName, String[] titles, Class<E> objectType, Iterator<E> objectIterator) {
+        Sheet sheet = getWorkbook().createSheet(sheetName);
+        AtomicInteger rowIndex = new AtomicInteger(0);
+        ObjectParam objectParam = ObjectParam.get(objectType);
+        //生成标题
+        writeTitle(sheet, rowIndex, titles, objectParam.getColumnNames().length);
+        //生成表头
+        writeColumnName(sheet, rowIndex, objectParam.getColumnNames());
+        //生成数据
+        while (objectIterator.hasNext()) {
+            writeObject(sheet, rowIndex, objectParam, objectIterator.next());
+        }
+    }
+
+    private void writeTitle(Sheet sheet, AtomicInteger rowIndex, String[] titles, int columnCount) {
+        if (titles != null && titles.length > 0) {
+            for (String title : titles) {
+                Row row = sheet.createRow(rowIndex.getAndIncrement());
+                Cell cell = row.createCell(0);
+                cell.setCellValue(title);
+                CellStyle style = getWorkbook().createCellStyle();
+                style.setAlignment(HorizontalAlignment.CENTER);
+                cell.setCellStyle(style);
+                //根据表头宽度合并单元格
+                if (columnCount > 1) {
+                    sheet.addMergedRegion(new CellRangeAddress(row.getRowNum(), row.getRowNum(), 0, columnCount - 1));
+                }
+            }
+        }
+    }
+
+    private void writeColumnName(Sheet sheet, AtomicInteger rowIndex, String[] names) {
+        Row columnNameRow = sheet.createRow(rowIndex.getAndIncrement());
+        int columnIndex = 0;
+        for (String name : names) {
+            Cell cell = columnNameRow.createCell(columnIndex++);
+            cell.setCellValue(name);
+        }
+    }
+
+    private <E> void writeObject(Sheet sheet, AtomicInteger rowIndex, ObjectParam objectParam, E object) {
+        Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
+        int columnIndex = 0;
+        for (String name : objectParam.getColumnNames()) {
+            Cell cell = dataRow.createCell(columnIndex++);
+            writeCell(cell, object, objectParam.get(name));
+        }
+    }
+
+    private void writeData(Sheet sheet, AtomicInteger rowIndex, String[] columnNames, DataMap dataMap) {
+        Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
+        int columnIndex = 0;
+        for (String name : columnNames) {
+            Cell cell = dataRow.createCell(columnIndex++);
+            cell.setCellValue(dataMap.getOrDefault(name, StringUtils.EMPTY));
+        }
+    }
+
+    private void writeArray(Sheet sheet, AtomicInteger rowIndex, String[] columnNames, String[] data) {
+        Row dataRow = sheet.createRow(rowIndex.getAndIncrement());
+        for (int columnIndex = 0; columnIndex < columnNames.length; columnIndex++) {
+            Cell cell = dataRow.createCell(columnIndex);
+            cell.setCellValue(data.length > columnIndex ? data[columnIndex] : StringUtils.EMPTY);
+        }
+    }
+
+    private <E> void writeCell(Cell cell, E object, FieldParam param) {
+        try {
+            Object value = param.getField().get(object);
+            if (value != null) {
+                ValueConvertor formator = ValueConvertors.getConvertor(param.getField().getType());
+                if (formator != null) {
+                    formator.format(value, param, cell);
+                } else {
+                    cell.setCellValue(String.valueOf(value));
+                }
+            } else {
+                cell.setCellValue(StringUtils.EMPTY);
+            }
+        } catch (Exception e) {
+            cell.setCellValue(StringUtils.EMPTY);
+        }
+    }
+
+    protected abstract Workbook getWorkbook();
+
+    private CellStyle createCellStyle() {
+        return getWorkbook().createCellStyle();
+    }
+
+    public void output(OutputStream ous) throws IOException {
+        getWorkbook().write(ous);
+    }
+
+}

+ 13 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/ValueConvertor.java

@@ -0,0 +1,13 @@
+package com.qmth.teachcloud.common.util.excel.convertor;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public interface ValueConvertor<E> {
+
+    E parse(String value, FieldParam param) throws Exception;
+
+    void format(E object, FieldParam param, Cell cell) throws Exception;
+
+}

+ 50 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/ValueConvertors.java

@@ -0,0 +1,50 @@
+package com.qmth.teachcloud.common.util.excel.convertor;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.qmth.teachcloud.common.util.excel.convertor.impl.*;
+public class ValueConvertors {
+
+    private static final Map<Class<?>, ValueConvertor<?>> defaultMap = new HashMap<>();
+
+    private static final Map<Class<?>, ValueConvertor<?>> extendMap = new ConcurrentHashMap<>();
+
+    static {
+        defaultMap.put(boolean.class, BooleanConvertor.instance);
+        defaultMap.put(Boolean.class, BooleanConvertor.instance);
+        defaultMap.put(int.class, IntegerConvertor.instance);
+        defaultMap.put(Integer.class, IntegerConvertor.instance);
+        defaultMap.put(long.class, LongConvertor.instance);
+        defaultMap.put(Long.class, LongConvertor.instance);
+        defaultMap.put(float.class, FloatConvertor.instance);
+        defaultMap.put(Float.class, FloatConvertor.instance);
+        defaultMap.put(double.class, DoubleConvertor.instance);
+        defaultMap.put(Double.class, DoubleConvertor.instance);
+        defaultMap.put(BigDecimal.class, BigDecimalConvertor.instance);
+        defaultMap.put(String.class, StringConvertor.instance);
+        defaultMap.put(Date.class, DateConvertor.instance);
+        defaultMap.put(LocalDateTime.class, LocalDateTimeConvertor.instance);
+    }
+
+    public static void register(Class<?> type, ValueConvertor<?> parser) {
+        extendMap.computeIfAbsent(type, key -> parser);
+    }
+
+    public static boolean containsType(Class<?> type) {
+        return defaultMap.containsKey(type) || extendMap.containsKey(type);
+    }
+
+    public static ValueConvertor<?> getConvertor(Class<?> type) {
+        ValueConvertor<?> convertor = defaultMap.get(type);
+        if (convertor == null) {
+            convertor = extendMap.get(type);
+        }
+        return convertor;
+    }
+
+}

+ 23 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/BigDecimalConvertor.java

@@ -0,0 +1,23 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import java.math.BigDecimal;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class BigDecimalConvertor implements ValueConvertor<BigDecimal> {
+
+    public static final BigDecimalConvertor instance = new BigDecimalConvertor();
+
+    @Override
+    public BigDecimal parse(String value, FieldParam param) throws Exception {
+        return new BigDecimal(value);
+    }
+
+    @Override
+    public void format(BigDecimal object, FieldParam param, Cell cell) throws Exception {
+        cell.setCellValue(object.stripTrailingZeros().toString());
+    }
+}

+ 45 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/BooleanConvertor.java

@@ -0,0 +1,45 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class BooleanConvertor implements ValueConvertor<Boolean> {
+
+    public static final BooleanConvertor instance = new BooleanConvertor();
+
+    public static final String[] TRUE_TEXT = { "是", "yes", "true" };
+
+    public static final String[] FALSE_TEXT = { "否", "no", "false" };
+
+    @Override
+    public Boolean parse(String value, FieldParam param) throws Exception {
+        if (value != null) {
+            if (match(value, TRUE_TEXT)) {
+                return true;
+            } else if (match(value, FALSE_TEXT)) {
+                return false;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void format(Boolean object, FieldParam param, Cell cell) throws Exception {
+        if (object) {
+            cell.setCellValue(TRUE_TEXT[0]);
+        } else {
+            cell.setCellValue(FALSE_TEXT[0]);
+        }
+    }
+
+    private boolean match(String value, String[] texts) {
+        for (String text : texts) {
+            if (text.equalsIgnoreCase(value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 33 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/DateConvertor.java

@@ -0,0 +1,33 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class DateConvertor implements ValueConvertor<Date> {
+
+    public static final DateConvertor instance = new DateConvertor();
+    String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
+    @Override
+    public Date parse(String value, FieldParam param) throws Exception {
+        String pattern = param.getAnnotation().pattern();
+        if (StringUtils.isBlank(pattern)) {
+            pattern = DEFAULT_DATE_PATTERN;
+        }
+        return new SimpleDateFormat(pattern).parse(value);
+    }
+
+    @Override
+    public void format(Date object, FieldParam param, Cell cell) throws Exception {
+        String pattern = param.getAnnotation().pattern();
+        if (StringUtils.isBlank(pattern)) {
+            pattern = DEFAULT_DATE_PATTERN;
+        }
+        cell.setCellValue(new SimpleDateFormat(pattern).format(object));
+    }
+}

+ 28 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/DoubleConvertor.java

@@ -0,0 +1,28 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import java.text.DecimalFormat;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class DoubleConvertor implements ValueConvertor<Double> {
+
+    public static final DoubleConvertor instance = new DoubleConvertor();
+
+    @Override
+    public Double parse(String value, FieldParam param) throws Exception {
+        return Double.parseDouble(value);
+    }
+
+    @Override
+    public void format(Double object, FieldParam param, Cell cell) throws Exception {
+        if (StringUtils.isNotBlank(param.getAnnotation().pattern())) {
+            cell.setCellValue(new DecimalFormat(param.getAnnotation().pattern()).format(object));
+        } else {
+            cell.setCellValue(object);
+        }
+    }
+}

+ 28 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/FloatConvertor.java

@@ -0,0 +1,28 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import java.text.DecimalFormat;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class FloatConvertor implements ValueConvertor<Float> {
+
+    public static final FloatConvertor instance = new FloatConvertor();
+
+    @Override
+    public Float parse(String value, FieldParam param) throws Exception {
+        return Float.parseFloat(value);
+    }
+
+    @Override
+    public void format(Float object, FieldParam param, Cell cell) throws Exception {
+        if (StringUtils.isNotBlank(param.getAnnotation().pattern())) {
+            cell.setCellValue(new DecimalFormat(param.getAnnotation().pattern()).format(object));
+        } else {
+            cell.setCellValue(object);
+        }
+    }
+}

+ 26 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/IntegerConvertor.java

@@ -0,0 +1,26 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class IntegerConvertor implements ValueConvertor<Integer> {
+
+    public static final IntegerConvertor instance = new IntegerConvertor();
+
+    @Override
+    public Integer parse(String value, FieldParam param) throws Exception {
+        return Integer.parseInt(value);
+    }
+
+    @Override
+    public void format(Integer object, FieldParam param, Cell cell) throws Exception {
+        //        if (StringUtils.isNotBlank(param.getAnnotation().pattern())) {
+        //            cell.setCellValue(new DecimalFormat(param.getAnnotation().pattern()).format(object));
+        //        } else {
+        //            cell.setCellValue(object);
+        //        }
+        cell.setCellValue(object);
+    }
+}

+ 33 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/LocalDateTimeConvertor.java

@@ -0,0 +1,33 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class LocalDateTimeConvertor implements ValueConvertor<LocalDateTime> {
+
+    public static final LocalDateTimeConvertor instance = new LocalDateTimeConvertor();
+    String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
+    @Override
+    public LocalDateTime parse(String value, FieldParam param) throws Exception {
+        String pattern = param.getAnnotation().pattern();
+        if (StringUtils.isBlank(pattern)) {
+            pattern = DEFAULT_DATE_PATTERN;
+        }
+        return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern));
+    }
+
+    @Override
+    public void format(LocalDateTime object, FieldParam param, Cell cell) throws Exception {
+        String pattern = param.getAnnotation().pattern();
+        if (StringUtils.isBlank(pattern)) {
+            pattern = DEFAULT_DATE_PATTERN;
+        }
+        cell.setCellValue(object.format(DateTimeFormatter.ofPattern(pattern)));
+    }
+}

+ 21 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/LongConvertor.java

@@ -0,0 +1,21 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class LongConvertor implements ValueConvertor<Long> {
+
+    public static final LongConvertor instance = new LongConvertor();
+
+    @Override
+    public Long parse(String value, FieldParam param) throws Exception {
+        return Long.parseLong(value);
+    }
+
+    @Override
+    public void format(Long object, FieldParam param, Cell cell) throws Exception {
+        cell.setCellValue(String.valueOf(object));
+    }
+}

+ 21 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/StringConvertor.java

@@ -0,0 +1,21 @@
+package com.qmth.teachcloud.common.util.excel.convertor.impl;
+
+import org.apache.poi.ss.usermodel.Cell;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.model.FieldParam;
+
+public class StringConvertor implements ValueConvertor<String> {
+
+    public static final StringConvertor instance = new StringConvertor();
+
+    @Override
+    public String parse(String value, FieldParam param) throws Exception {
+        return value;
+    }
+
+    @Override
+    public void format(String object, FieldParam param, Cell cell) throws Exception {
+        cell.setCellValue(object);
+    }
+}

+ 9 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/DataMap.java

@@ -0,0 +1,9 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+import java.util.HashMap;
+
+public class DataMap extends HashMap<String, String> {
+
+    private static final long serialVersionUID = -432649361390941243L;
+
+}

+ 51 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelColumn.java

@@ -0,0 +1,51 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+import java.lang.annotation.*;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+
+/**
+ * Excel数据列与bean字段对应关系设置
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD })
+@Documented
+public @interface ExcelColumn {
+
+    /**
+     * 字段对应的列名
+     *
+     * @return
+     */
+    String name();
+
+    /**
+     * 输出Excel时各列的顺序,数值越小越靠前
+     *
+     * @return
+     */
+    int index();
+
+    /**
+     * 字段是否允许为空
+     *
+     * @return
+     */
+    boolean nullable() default false;
+
+    /**
+     * 自定义解析/输出使用的格式<br/>
+     * 目前只针对Date, LocalDateTime, double, Double, float, Float字段类型有效
+     *
+     * @return
+     */
+    String pattern() default "";
+
+    /**
+     * 自定义解析与格式化输出工具
+     *
+     * @return
+     */
+    Class<? extends ValueConvertor> convertor() default ValueConvertor.class;
+
+}

+ 6 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelConstants.java

@@ -0,0 +1,6 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+public interface ExcelConstants {
+
+    String DEFAULT_DATE_PATTERN = "yyyy-MM-dd HH:mm:ss";
+}

+ 9 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelType.java

@@ -0,0 +1,9 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+/**
+ * EXCEL文件格式分类
+ */
+public enum ExcelType {
+
+    XLS, XLSX;
+}

+ 24 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/FieldParam.java

@@ -0,0 +1,24 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+import java.lang.reflect.Field;
+
+public class FieldParam {
+
+    private Field field;
+
+    private ExcelColumn annotation;
+
+    public FieldParam(Field field, ExcelColumn annotation) {
+        this.field = field;
+        this.annotation = annotation;
+    }
+
+    public Field getField() {
+        return field;
+    }
+
+    public ExcelColumn getAnnotation() {
+        return annotation;
+    }
+
+}

+ 83 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ObjectParam.java

@@ -0,0 +1,83 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertor;
+import com.qmth.teachcloud.common.util.excel.convertor.ValueConvertors;
+
+public class ObjectParam extends HashMap<String, FieldParam> {
+
+    private static final long serialVersionUID = 3789838085423920607L;
+
+
+    private static Map<Class<?>, ObjectParam> paramMap = new ConcurrentHashMap<>();
+
+    public static ObjectParam get(Class<?> clazz) {
+        ObjectParam param = paramMap.get(clazz);
+        if (param == null) {
+            param = paramMap.computeIfAbsent(clazz, ObjectParam::new);
+        }
+        return param;
+    }
+
+    private String[] columnNames;
+
+    private List<FieldParam> fieldList;
+
+    private ObjectParam(Class<?> objectType) {
+        super();
+
+        Field[] fields = objectType.getDeclaredFields();
+        for (Field field : fields) {
+            field.setAccessible(true);
+            ExcelColumn annotation = field.getAnnotation(ExcelColumn.class);
+            if (annotation != null) {
+                if (!ValueConvertors.containsType(field.getType())) {
+                    if (!annotation.convertor().equals(ValueConvertor.class)) {
+                        try {
+                            ValueConvertors.register(field.getType(), annotation.convertor().newInstance());
+                        } catch (Exception e) {
+                            throw new RuntimeException(
+                                    "Unsupported convertor in @ExcelColumn: " + annotation.convertor());
+                        }
+                    } else {
+                        throw new RuntimeException("Unsupported field type with @ExcelColumn: " + field.getType());
+                    }
+                }
+                put(annotation.name(), new FieldParam(field, annotation));
+            }
+        }
+
+        fieldList = new ArrayList<>(values());
+        fieldList.sort(Comparator.comparingInt(p -> p.getAnnotation().index()));
+        columnNames = fieldList.stream().map(p -> p.getAnnotation().name()).toArray(String[]::new);
+    }
+
+    public String[] getColumnNames() {
+        return columnNames;
+    }
+
+    /**
+     * 验证解析的列名,需要所有非空字段对应的列名存在
+     * 返回缺少的列名,不缺时返回集合为空
+     *
+     * @param columnNames
+     * @return
+     */
+    public Set<String> validate(String[] columnNames) {
+        Set<String> columnSet = fieldList.stream().filter(p -> !p.getAnnotation().nullable())
+                .map(p -> p.getAnnotation().name()).collect(Collectors.toCollection(LinkedHashSet::new));
+        if (ArrayUtils.isEmpty(columnNames)) {
+            return columnSet;
+        }
+        for (String name : columnNames) {
+            columnSet.remove(name);
+        }
+        return columnSet;
+    }
+}

+ 21 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/XlsWriter.java

@@ -0,0 +1,21 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Workbook;
+
+import com.qmth.teachcloud.common.util.excel.ExcelWriterDynamic;
+
+public class XlsWriter extends ExcelWriterDynamic {
+
+    private HSSFWorkbook workbook;
+
+    public XlsWriter() {
+        this.workbook = new HSSFWorkbook();
+    }
+
+    @Override
+    public Workbook getWorkbook() {
+        return workbook;
+    }
+
+}

+ 29 - 0
teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/XlsxWriter.java

@@ -0,0 +1,29 @@
+package com.qmth.teachcloud.common.util.excel.model;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+
+import com.qmth.teachcloud.common.util.excel.ExcelWriterDynamic;
+
+public class XlsxWriter extends ExcelWriterDynamic {
+
+    private SXSSFWorkbook workbook;
+
+    public XlsxWriter() {
+        this.workbook = new SXSSFWorkbook(100);
+    }
+
+    @Override
+    public Workbook getWorkbook() {
+        return workbook;
+    }
+
+    @Override
+    public void output(OutputStream ous) throws IOException {
+        super.output(ous);
+        workbook.dispose();
+    }
+}

+ 63 - 1
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/bean/archivescore/ArchiveStudentVo.java

@@ -1,12 +1,18 @@
 package com.qmth.teachcloud.mark.bean.archivescore;
 
+import java.util.LinkedList;
 import java.util.List;
 
+import org.apache.commons.lang3.StringUtils;
+
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.qmth.teachcloud.common.annotation.ExcelProperty;
+import com.qmth.teachcloud.mark.dto.mark.ScoreItem;
 
 public class ArchiveStudentVo {
+
+    public static final String SPLIT = ";";
     @JsonSerialize(using = ToStringSerializer.class)
     private Long studentId;
 
@@ -23,9 +29,17 @@ public class ArchiveStudentVo {
     private String courseCode;
     @ExcelProperty(name = "课程名称", width = 20, index = 6)
     private String courseName;
-    @ExcelProperty(name = "成绩", width = 20, index = 7)
+    @ExcelProperty(name = "客观分", width = 20, index = 7)
+    private Double objectiveScore;
+    @ExcelProperty(name = "主观分", width = 20, index = 8)
+    private Double subjectiveScore;
+    @ExcelProperty(name = "成绩", width = 20, index = 9)
     private Double totalScore;
 
+    private String subjectiveScoreList;
+
+    private String objectiveScoreList;
+
     private List<String> sheetUrls;
 
     private String sheetPath;
@@ -135,4 +149,52 @@ public class ArchiveStudentVo {
     public void setExamEndTime(Long examEndTime) {
         this.examEndTime = examEndTime;
     }
+
+    public Double getObjectiveScore() {
+        return objectiveScore;
+    }
+
+    public void setObjectiveScore(Double objectiveScore) {
+        this.objectiveScore = objectiveScore;
+    }
+
+    public Double getSubjectiveScore() {
+        return subjectiveScore;
+    }
+
+    public void setSubjectiveScore(Double subjectiveScore) {
+        this.subjectiveScore = subjectiveScore;
+    }
+
+    public String getSubjectiveScoreList() {
+        return subjectiveScoreList;
+    }
+
+    public void setSubjectiveScoreList(String subjectiveScoreList) {
+        this.subjectiveScoreList = subjectiveScoreList;
+    }
+
+    public String getObjectiveScoreList() {
+        return objectiveScoreList;
+    }
+
+    public void setObjectiveScoreList(String objectiveScoreList) {
+        this.objectiveScoreList = objectiveScoreList;
+    }
+
+    public List<ScoreItem> getScoreList(boolean objective) {
+        List<ScoreItem> scoreList = new LinkedList<ScoreItem>();
+        try {
+            String[] values = StringUtils.split(objective ? objectiveScoreList : subjectiveScoreList, SPLIT);
+            for (String value : values) {
+                ScoreItem item = ScoreItem.parse(value, objective);
+                item.setObjective(objective);
+                if (item != null) {
+                    scoreList.add(item);
+                }
+            }
+        } catch (Exception e) {
+        }
+        return scoreList;
+    }
 }

+ 51 - 1
teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkStudentServiceImpl.java

@@ -1,11 +1,13 @@
 package com.qmth.teachcloud.mark.service.impl;
 
 import java.io.File;
+import java.net.URLEncoder;
 import java.nio.charset.StandardCharsets;
 import java.util.*;
 import java.util.stream.Collectors;
 
 import javax.annotation.Resource;
+import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.constraints.NotNull;
 
@@ -41,6 +43,8 @@ import com.qmth.teachcloud.common.enums.mark.SubjectiveStatus;
 import com.qmth.teachcloud.common.service.BasicRoleDataPermissionService;
 import com.qmth.teachcloud.common.service.TeachcloudCommonService;
 import com.qmth.teachcloud.common.util.*;
+import com.qmth.teachcloud.common.util.excel.ExcelWriterDynamic;
+import com.qmth.teachcloud.common.util.excel.model.ExcelType;
 import com.qmth.teachcloud.mark.bean.UpdateTimeVo;
 import com.qmth.teachcloud.mark.bean.archivescore.*;
 import com.qmth.teachcloud.mark.bean.omredit.OmrEditDomain;
@@ -889,9 +893,55 @@ public class MarkStudentServiceImpl extends ServiceImpl<MarkStudentMapper, MarkS
 
     @Override
     public void scoreExport(ArchiveStudentQuery query, HttpServletResponse response) {
+        //生成表头
+        String[] columnName= new String[]{"学生姓名", "学号", "学院", "班级", "课程代码", "课程名称", "客观分", "主观分",
+                "成绩"};
+        List<MarkQuestion> oQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(query.getExamId(), query.getPaperNumber(), null, true);
+        List<MarkQuestion> sQuestionList = markQuestionService.listQuestionByExamIdAndPaperNumberAndGroupNumber(query.getExamId(), query.getPaperNumber(), null, false);
+        List<String> columnNameList =Arrays.asList(columnName);
+        for (MarkQuestion question:oQuestionList) {
+            columnNameList.add(question.getMainTitle() + " " + question.getMainNumber() + "-" + question.getSubNumber() +"选项");
+            columnNameList.add(question.getMainTitle() + " " + question.getMainNumber() + "-" + question.getSubNumber() +"得分");
+        }
+        for (MarkQuestion question:oQuestionList) {
+            columnNameList.add(question.getMainTitle() + " " + question.getMainNumber() + "-" + question.getSubNumber());
+        }
+        String[] columnNames =columnNameList.toArray(new String[0]);
+        //生成动态内容
+         List<String[]> columnValues=new ArrayList<>();
         List<ArchiveStudentVo> ret = baseMapper.studentList(query);
+        for (ArchiveStudentVo s: ret ) {
+            List<String> valueList =new ArrayList<>();
+            valueList.add(s.getStudentName());
+            valueList.add(s.getStudentCode());
+            valueList.add(s.getCollege());
+            valueList.add(s.getClassName());
+            valueList.add(s.getCourseCode());
+            valueList.add(s.getCourseName());
+            valueList.add(s.getObjectiveScore().toString());
+            valueList.add(s.getSubjectiveScore().toString());
+            valueList.add(s.getTotalScore().toString());
+            for (ScoreItem item : s.getScoreList(true)) {
+                valueList.add(item.getAnswer());
+                valueList.add(item.getScore().toString());
+            }
+            for (ScoreItem item  : s.getScoreList(false)) {
+                valueList.add(item.getScore().toString());
+            }
+            String[] columnValue =valueList.toArray(new String[0]);
+            columnValues.add(columnValue);
+        }
         try {
-            ExcelUtil.excelExport("成绩导出", ArchiveStudentVo.class, ret, response);
+            log.debug("导出Excel开始...");
+            response.setHeader("Content-Disposition", "inline;filename=" + URLEncoder.encode("成绩导出", SystemConstant.CHARSET_NAME) + ".xlsx");
+            response.setContentType("application/vnd.ms-excel");
+            ServletOutputStream outputStream = response.getOutputStream();
+            ExcelWriterDynamic writer = ExcelWriterDynamic.create(ExcelType.XLSX);
+            writer.writeDataArrays("成绩导出", null,columnNames ,columnValues.listIterator());
+            writer.output(outputStream);
+            outputStream.flush();
+            outputStream.close();
+            log.debug("导出Excel结束");
         } catch (Exception e) {
             throw new RuntimeException(e);
         }