Преглед изворни кода

Merge remote-tracking branch 'origin/dev_v3.3.1' into dev_v3.3.1

wangliang пре 1 година
родитељ
комит
0356969853
47 измењених фајлова са 2002 додато и 149 уклоњено
  1. 23 42
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CourseDimensionDto.java
  2. 66 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CourseDimensionImportDto.java
  3. 32 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CourseDimensionOccupiedDto.java
  4. 94 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/params/CourseTargetParam.java
  5. 80 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/result/CourseDimensionTree.java
  6. 73 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/result/CourseTargetResult.java
  7. 36 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseDimension.java
  8. 36 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseEvaluation.java
  9. 24 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseTarget.java
  10. 26 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseWeight.java
  11. 42 6
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/TeachCourse.java
  12. 20 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/CourseDimensionSourceEnum.java
  13. 20 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/CourseEvaluationTypeEnum.java
  14. 20 0
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/CourseSettingTypeEnum.java
  15. 42 2
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/CourseTargetService.java
  16. 19 1
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/TeachCourseService.java
  17. 99 12
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/CourseDimensionServiceImpl.java
  18. 206 1
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/CourseTargetServiceImpl.java
  19. 2 4
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/ExamTaskServiceImpl.java
  20. 81 65
      distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/TeachCourseServiceImpl.java
  21. 38 1
      distributed-print-business/src/main/resources/db/log/脚本-caozx.sql
  22. 6 0
      distributed-print/install/mysql/upgrade/3.3.1.sql
  23. 1 1
      distributed-print/src/main/java/com/qmth/distributed/print/api/BasicExamStudentController.java
  24. 68 10
      distributed-print/src/main/java/com/qmth/distributed/print/api/CourseTargetController.java
  25. 0 2
      distributed-print/src/main/java/com/qmth/distributed/print/api/ExamTaskApplyController.java
  26. 181 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/ExcelWriterDynamic.java
  27. 13 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/ValueConvertor.java
  28. 50 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/ValueConvertors.java
  29. 23 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/BigDecimalConvertor.java
  30. 45 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/BooleanConvertor.java
  31. 33 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/DateConvertor.java
  32. 28 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/DoubleConvertor.java
  33. 28 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/FloatConvertor.java
  34. 26 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/IntegerConvertor.java
  35. 33 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/LocalDateTimeConvertor.java
  36. 21 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/LongConvertor.java
  37. 21 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/convertor/impl/StringConvertor.java
  38. 9 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/DataMap.java
  39. 51 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelColumn.java
  40. 6 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelConstants.java
  41. 9 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ExcelType.java
  42. 24 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/FieldParam.java
  43. 83 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/ObjectParam.java
  44. 21 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/XlsWriter.java
  45. 29 0
      teachcloud-common/src/main/java/com/qmth/teachcloud/common/util/excel/model/XlsxWriter.java
  46. 63 1
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/bean/archivescore/ArchiveStudentVo.java
  47. 51 1
      teachcloud-mark/src/main/java/com/qmth/teachcloud/mark/service/impl/MarkStudentServiceImpl.java

+ 23 - 42
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CourseDimensionDto.java

@@ -1,66 +1,47 @@
 package com.qmth.distributed.print.business.bean.dto;
 
-import com.qmth.teachcloud.common.annotation.ExcelNote;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import io.swagger.annotations.ApiModelProperty;
 
-import javax.validation.constraints.NotNull;
-
 /**
- * @Description: 课程知识点数据导入dto
+ * @Description: 毕业要求dto
  * @Author: CaoZixuan
- * @Date: 2024-02-27
+ * @Date: 2024-02-28
  */
 public class CourseDimensionDto {
 
-    @ApiModelProperty("课程代码")
-    @ExcelNote(value = "课程代码")
-    @NotNull
-    private String courseCode;
-
-    @ApiModelProperty("课程名称")
-    @ExcelNote(value = "课程名称")
-    @NotNull
-    private String courseName;
-
-    @ApiModelProperty("一级属性内容")
-    @ExcelNote(value = "一级属性内容")
-    @NotNull
-    private String firstDimensionName;
+    @ApiModelProperty("课程知识点id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
 
-    @ApiModelProperty("二级属性内容")
-    @ExcelNote(value = "二级属性内容")
-    @NotNull
-    private String secondDimensionName;
+    @ApiModelProperty("课程知识点代码")
+    private String code;
 
-    public String getCourseCode() {
-        return courseCode;
-    }
-
-    public void setCourseCode(String courseCode) {
-        this.courseCode = courseCode;
-    }
+    @ApiModelProperty("课程知识点名称")
+    private String name;
 
-    public String getCourseName() {
-        return courseName;
+    public Long getId() {
+        return id;
     }
 
-    public void setCourseName(String courseName) {
-        this.courseName = courseName;
+    public void setId(Long id) {
+        this.id = id;
     }
 
-    public String getFirstDimensionName() {
-        return firstDimensionName;
+    public String getCode() {
+        return code;
     }
 
-    public void setFirstDimensionName(String firstDimensionName) {
-        this.firstDimensionName = firstDimensionName;
+    public void setCode(String code) {
+        this.code = code;
     }
 
-    public String getSecondDimensionName() {
-        return secondDimensionName;
+    public String getName() {
+        return name;
     }
 
-    public void setSecondDimensionName(String secondDimensionName) {
-        this.secondDimensionName = secondDimensionName;
+    public void setName(String name) {
+        this.name = name;
     }
 }

+ 66 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CourseDimensionImportDto.java

@@ -0,0 +1,66 @@
+package com.qmth.distributed.print.business.bean.dto;
+
+import com.qmth.teachcloud.common.annotation.ExcelNote;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotNull;
+
+/**
+ * @Description: 课程知识点数据导入dto
+ * @Author: CaoZixuan
+ * @Date: 2024-02-27
+ */
+public class CourseDimensionImportDto {
+
+    @ApiModelProperty("课程代码")
+    @ExcelNote(value = "课程代码")
+    @NotNull
+    private String courseCode;
+
+    @ApiModelProperty("课程名称")
+    @ExcelNote(value = "课程名称")
+    @NotNull
+    private String courseName;
+
+    @ApiModelProperty("一级属性内容")
+    @ExcelNote(value = "一级属性内容")
+    @NotNull
+    private String firstDimensionName;
+
+    @ApiModelProperty("二级属性内容")
+    @ExcelNote(value = "二级属性内容")
+    @NotNull
+    private String secondDimensionName;
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getCourseName() {
+        return courseName;
+    }
+
+    public void setCourseName(String courseName) {
+        this.courseName = courseName;
+    }
+
+    public String getFirstDimensionName() {
+        return firstDimensionName;
+    }
+
+    public void setFirstDimensionName(String firstDimensionName) {
+        this.firstDimensionName = firstDimensionName;
+    }
+
+    public String getSecondDimensionName() {
+        return secondDimensionName;
+    }
+
+    public void setSecondDimensionName(String secondDimensionName) {
+        this.secondDimensionName = secondDimensionName;
+    }
+}

+ 32 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/dto/CourseDimensionOccupiedDto.java

@@ -0,0 +1,32 @@
+package com.qmth.distributed.print.business.bean.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * @Description: 课程知识点占用dto
+ * @Author: CaoZixuan
+ * @Date: 2024-02-28
+ */
+public class CourseDimensionOccupiedDto {
+    @ApiModelProperty("课程目标id")
+    private Long courseTargetId;
+
+    @ApiModelProperty("知识点占用状态 true:被占用 false:未占用")
+    private Boolean status;
+
+    public Long getCourseTargetId() {
+        return courseTargetId;
+    }
+
+    public void setCourseTargetId(Long courseTargetId) {
+        this.courseTargetId = courseTargetId;
+    }
+
+    public Boolean getStatus() {
+        return status;
+    }
+
+    public void setStatus(Boolean status) {
+        this.status = status;
+    }
+}

+ 94 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/params/CourseTargetParam.java

@@ -0,0 +1,94 @@
+package com.qmth.distributed.print.business.bean.params;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.teachcloud.common.annotation.EditKey;
+import io.swagger.annotations.ApiModelProperty;
+import org.hibernate.validator.constraints.Length;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+/**
+ * @Description: 课程目标参数
+ * @Author: CaoZixuan
+ * @Date: 2024-02-29
+ */
+public class CourseTargetParam {
+
+    @ApiModelProperty("课程目标id(编辑必填)")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @EditKey
+    private Long id;
+
+    @ApiModelProperty("考试ID")
+    @JsonSerialize(using = ToStringSerializer.class)
+    @NotNull(message = "缺少考试")
+    @Range(min = 1L, message = "缺少考试")
+    private Long examId;
+
+    @ApiModelProperty("课程编号(新增必填)")
+    @NotNull(message = "缺少课程编号")
+    @Length(min = 1, message = "缺少课程编号")
+    private String courseCode;
+
+    @ApiModelProperty("课程目标名称")
+    @NotNull(message = "请输入课程目标")
+    @Length(min = 1, message = "请输入课程目标")
+    private String targetName;
+
+    @ApiModelProperty("毕业要求")
+    private String degreeRequirement;
+
+    @ApiModelProperty("知识点id集合")
+    private List<Long> dimensionIdList;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getExamId() {
+        return examId;
+    }
+
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
+    }
+
+    public String getTargetName() {
+        return targetName;
+    }
+
+    public void setTargetName(String targetName) {
+        this.targetName = targetName;
+    }
+
+    public String getDegreeRequirement() {
+        return degreeRequirement;
+    }
+
+    public void setDegreeRequirement(String degreeRequirement) {
+        this.degreeRequirement = degreeRequirement;
+    }
+
+    public List<Long> getDimensionIdList() {
+        return dimensionIdList;
+    }
+
+    public void setDimensionIdList(List<Long> dimensionIdList) {
+        this.dimensionIdList = dimensionIdList;
+    }
+}

+ 80 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/result/CourseDimensionTree.java

@@ -0,0 +1,80 @@
+package com.qmth.distributed.print.business.bean.result;
+
+import com.qmth.distributed.print.business.bean.dto.CourseDimensionOccupiedDto;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+
+/**
+ * @Description: 课程知识点树结果
+ * @Author: CaoZixuan
+ * @Date: 2024-02-28
+ */
+public class CourseDimensionTree {
+    @ApiModelProperty("知识点id")
+    private Long id;
+
+    @ApiModelProperty("知识点编号")
+    private String code;
+
+    @ApiModelProperty("知识点名称")
+    private String name;
+
+    @ApiModelProperty("占用状态")
+    private CourseDimensionOccupiedDto occupied;
+
+    @ApiModelProperty("子知识点")
+    private List<CourseDimensionTree> children;
+
+    public CourseDimensionTree(Long id, String code, String name, CourseDimensionOccupiedDto occupied,
+            List<CourseDimensionTree> children) {
+        this.id = id;
+        this.code = code;
+        this.name = name;
+        this.occupied = occupied;
+        this.children = children;
+    }
+
+    public CourseDimensionTree() {
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public CourseDimensionOccupiedDto getOccupied() {
+        return occupied;
+    }
+
+    public void setOccupied(CourseDimensionOccupiedDto occupied) {
+        this.occupied = occupied;
+    }
+
+    public List<CourseDimensionTree> getChildren() {
+        return children;
+    }
+
+    public void setChildren(List<CourseDimensionTree> children) {
+        this.children = children;
+    }
+}

+ 73 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/bean/result/CourseTargetResult.java

@@ -0,0 +1,73 @@
+package com.qmth.distributed.print.business.bean.result;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.distributed.print.business.bean.dto.CourseDimensionDto;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.List;
+
+/**
+ * @Description: 课程目标查询结果
+ * @Author: CaoZixuan
+ * @Date: 2024-02-28
+ */
+public class CourseTargetResult {
+    @ApiModelProperty("课程目标id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long id;
+
+    @ApiModelProperty("课程目标名称")
+    private String targetName;
+
+    @ApiModelProperty("毕业要求指标")
+    @JsonIgnore
+    private String targetContent;
+
+    @ApiModelProperty("毕业要求")
+    private String degreeRequirement;
+
+    @ApiModelProperty("课程目标毕业要求")
+    private List<CourseDimensionDto> dimensionList;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getTargetName() {
+        return targetName;
+    }
+
+    public void setTargetName(String targetName) {
+        this.targetName = targetName;
+    }
+
+    public String getTargetContent() {
+        return targetContent;
+    }
+
+    public void setTargetContent(String targetContent) {
+        this.targetContent = targetContent;
+    }
+
+    public String getDegreeRequirement() {
+        return degreeRequirement;
+    }
+
+    public void setDegreeRequirement(String degreeRequirement) {
+        this.degreeRequirement = degreeRequirement;
+    }
+
+    public List<CourseDimensionDto> getDimensionList() {
+        return dimensionList;
+    }
+
+    public void setDimensionList(List<CourseDimensionDto> dimensionList) {
+        this.dimensionList = dimensionList;
+    }
+}

+ 36 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseDimension.java

@@ -2,6 +2,7 @@ package com.qmth.distributed.print.business.entity;
 
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.distributed.print.business.enums.CourseDimensionSourceEnum;
 import com.qmth.teachcloud.common.base.BaseEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -19,6 +20,10 @@ public class CourseDimension extends BaseEntity {
 
     private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "教学课程id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long teachCourseId;
+
     @ApiModelProperty(value = "学校id")
     @JsonSerialize(using = ToStringSerializer.class)
     private Long schoolId;
@@ -34,6 +39,13 @@ public class CourseDimension extends BaseEntity {
     @ApiModelProperty(value = "课程编号")
     private String courseCode;
 
+    @ApiModelProperty(value = "教师id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long userId;
+
+    @ApiModelProperty(value = "来源")
+    private CourseDimensionSourceEnum source;
+
     @ApiModelProperty(value = "知识点编号")
     private String code;
 
@@ -47,6 +59,14 @@ public class CourseDimension extends BaseEntity {
     @JsonSerialize(using = ToStringSerializer.class)
     private Long parentId;
 
+    public Long getTeachCourseId() {
+        return teachCourseId;
+    }
+
+    public void setTeachCourseId(Long teachCourseId) {
+        this.teachCourseId = teachCourseId;
+    }
+
     public Long getSchoolId() {
         return schoolId;
     }
@@ -79,6 +99,22 @@ public class CourseDimension extends BaseEntity {
         this.courseCode = courseCode;
     }
 
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public CourseDimensionSourceEnum getSource() {
+        return source;
+    }
+
+    public void setSource(CourseDimensionSourceEnum source) {
+        this.source = source;
+    }
+
     public String getCode() {
         return code;
     }

+ 36 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseEvaluation.java

@@ -2,6 +2,7 @@ package com.qmth.distributed.print.business.entity;
 
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.qmth.distributed.print.business.enums.CourseEvaluationTypeEnum;
 import com.qmth.teachcloud.common.base.BaseEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -19,6 +20,10 @@ public class CourseEvaluation extends BaseEntity {
 
     private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "教学课程id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long teachCourseId;
+
     @ApiModelProperty(value = "学校id")
     @JsonSerialize(using = ToStringSerializer.class)
     private Long schoolId;
@@ -30,12 +35,27 @@ public class CourseEvaluation extends BaseEntity {
     @ApiModelProperty(value = "课程编号")
     private String courseCode;
 
+    @ApiModelProperty(value = "教师id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long userId;
+
+    @ApiModelProperty(value = "类型(默认DEFAULT、自定义CUSTOM)")
+    private CourseEvaluationTypeEnum type;
+
     @ApiModelProperty(value = "评价方式")
     private String evaluation;
 
     @ApiModelProperty(value = "评价方式描述")
     private String evaluationDesc;
 
+    public Long getTeachCourseId() {
+        return teachCourseId;
+    }
+
+    public void setTeachCourseId(Long teachCourseId) {
+        this.teachCourseId = teachCourseId;
+    }
+
     public Long getSchoolId() {
         return schoolId;
     }
@@ -60,6 +80,22 @@ public class CourseEvaluation extends BaseEntity {
         this.courseCode = courseCode;
     }
 
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
+    public CourseEvaluationTypeEnum getType() {
+        return type;
+    }
+
+    public void setType(CourseEvaluationTypeEnum type) {
+        this.type = type;
+    }
+
     public String getEvaluation() {
         return evaluation;
     }

+ 24 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseTarget.java

@@ -21,6 +21,10 @@ public class CourseTarget extends BaseEntity {
 
     private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "教学课程id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long teachCourseId;
+
     @ApiModelProperty(value = "学校id")
     @JsonSerialize(using = ToStringSerializer.class)
     private Long schoolId;
@@ -32,6 +36,10 @@ public class CourseTarget extends BaseEntity {
     @ApiModelProperty(value = "课程编号")
     private String courseCode;
 
+    @ApiModelProperty(value = "教师id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long userId;
+
     @ApiModelProperty(value = "课程目标")
     private String targetName;
 
@@ -44,6 +52,14 @@ public class CourseTarget extends BaseEntity {
     @ApiModelProperty(value = "课程目标整体权重")
     private BigDecimal totalWeight;
 
+    public Long getTeachCourseId() {
+        return teachCourseId;
+    }
+
+    public void setTeachCourseId(Long teachCourseId) {
+        this.teachCourseId = teachCourseId;
+    }
+
     public Long getSchoolId() {
         return schoolId;
     }
@@ -68,6 +84,14 @@ public class CourseTarget extends BaseEntity {
         this.courseCode = courseCode;
     }
 
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
     public String getTargetName() {
         return targetName;
     }

+ 26 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/CourseWeight.java

@@ -1,5 +1,7 @@
 package com.qmth.distributed.print.business.entity;
 
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
 import com.qmth.teachcloud.common.base.BaseEntity;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
@@ -19,6 +21,10 @@ public class CourseWeight extends BaseEntity {
 
     private static final long serialVersionUID = 1L;
 
+    @ApiModelProperty(value = "教学课程id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long teachCourseId;
+
     @ApiModelProperty(value = "学校id")
     private Long schoolId;
 
@@ -28,6 +34,10 @@ public class CourseWeight extends BaseEntity {
     @ApiModelProperty(value = "课程编号")
     private String courseCode;
 
+    @ApiModelProperty(value = "教师id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long userId;
+
     @ApiModelProperty(value = "课程目标表id")
     private Long targetId;
 
@@ -43,6 +53,14 @@ public class CourseWeight extends BaseEntity {
     @ApiModelProperty(value = "目标分值")
     private BigDecimal targetScore;
 
+    public Long getTeachCourseId() {
+        return teachCourseId;
+    }
+
+    public void setTeachCourseId(Long teachCourseId) {
+        this.teachCourseId = teachCourseId;
+    }
+
     public Long getSchoolId() {
         return schoolId;
     }
@@ -67,6 +85,14 @@ public class CourseWeight extends BaseEntity {
         this.courseCode = courseCode;
     }
 
+    public Long getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Long userId) {
+        this.userId = userId;
+    }
+
     public Long getTargetId() {
         return targetId;
     }

+ 42 - 6
distributed-print-business/src/main/java/com/qmth/distributed/print/business/entity/TeachCourse.java

@@ -23,10 +23,19 @@ public class TeachCourse extends BaseEntity implements Serializable {
     @JsonSerialize(using = ToStringSerializer.class)
     private Long schoolId;
 
+    @ApiModelProperty(value = "考试id")
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long examId;
+
+    // TODO: 2024/2/28  删除该字段
+    @Deprecated
     @ApiModelProperty(value = "基础课程id")
     @JsonSerialize(using = ToStringSerializer.class)
     private Long basicCourseId;
 
+    @ApiModelProperty(value = "课程编号")
+    private String courseCode;
+
     @ApiModelProperty(value = "创建此教学课程的用户id")
     @JsonSerialize(using = ToStringSerializer.class)
     private Long userId;
@@ -34,11 +43,14 @@ public class TeachCourse extends BaseEntity implements Serializable {
     @ApiModelProperty(value = "是否删除")
     private Boolean enable;
 
-    public TeachCourse(){
+    @ApiModelProperty(value = "权重设置")
+    private Boolean weightSetting;
+
+    public TeachCourse() {
 
     }
 
-    public TeachCourse(SysUser sysUser, Long basicCourseId, Long teachId){
+    public TeachCourse(SysUser sysUser, Long basicCourseId, Long teachId) {
         setSchoolId(sysUser.getSchoolId());
         setBasicCourseId(basicCourseId);
         setUserId(teachId);
@@ -55,12 +67,20 @@ public class TeachCourse extends BaseEntity implements Serializable {
         this.schoolId = schoolId;
     }
 
-    public Long getBasicCourseId() {
-        return basicCourseId;
+    public Long getExamId() {
+        return examId;
     }
 
-    public void setBasicCourseId(Long basicCourseId) {
-        this.basicCourseId = basicCourseId;
+    public void setExamId(Long examId) {
+        this.examId = examId;
+    }
+
+    public String getCourseCode() {
+        return courseCode;
+    }
+
+    public void setCourseCode(String courseCode) {
+        this.courseCode = courseCode;
     }
 
     public Long getUserId() {
@@ -78,4 +98,20 @@ public class TeachCourse extends BaseEntity implements Serializable {
     public void setEnable(Boolean enable) {
         this.enable = enable;
     }
+
+    public Boolean getWeightSetting() {
+        return weightSetting;
+    }
+
+    public void setWeightSetting(Boolean weightSetting) {
+        this.weightSetting = weightSetting;
+    }
+
+    public Long getBasicCourseId() {
+        return basicCourseId;
+    }
+
+    public void setBasicCourseId(Long basicCourseId) {
+        this.basicCourseId = basicCourseId;
+    }
 }

+ 20 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/CourseDimensionSourceEnum.java

@@ -0,0 +1,20 @@
+package com.qmth.distributed.print.business.enums;
+
+/**
+ * @Description: 课程知识点来源
+ * @Author: CaoZixuan
+ * @Date: 2024-02-28
+ */
+public enum CourseDimensionSourceEnum {
+    UNION_QUESTION("独立题库"), IMPORT("导入");
+
+    private final String name;
+
+    CourseDimensionSourceEnum(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 20 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/CourseEvaluationTypeEnum.java

@@ -0,0 +1,20 @@
+package com.qmth.distributed.print.business.enums;
+
+/**
+ * @Description: 课程评价类型枚举
+ * @Author: CaoZixuan
+ * @Date: 2024-02-28
+ */
+public enum CourseEvaluationTypeEnum {
+    DEFAULT("默认"), CUSTOM("自定义");
+
+    private final String name;
+
+    CourseEvaluationTypeEnum(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 20 - 0
distributed-print-business/src/main/java/com/qmth/distributed/print/business/enums/CourseSettingTypeEnum.java

@@ -0,0 +1,20 @@
+package com.qmth.distributed.print.business.enums;
+
+/**
+ * @Description: 课程设置类型
+ * @Author: CaoZixuan
+ * @Date: 2024-02-29
+ */
+public enum CourseSettingTypeEnum {
+    COURSE_DIMENSION("课程知识点"), COURSE_TARGET("课程目标"), COURSE_EVALUATION("课程评价方式"), COURSE_WEIGHT("课程权重");
+
+    private final String name;
+
+    CourseSettingTypeEnum(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+}

+ 42 - 2
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/CourseTargetService.java

@@ -1,7 +1,13 @@
 package com.qmth.distributed.print.business.service;
 
-import com.qmth.distributed.print.business.entity.CourseTarget;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.distributed.print.business.bean.params.CourseTargetParam;
+import com.qmth.distributed.print.business.bean.result.CourseDimensionTree;
+import com.qmth.distributed.print.business.bean.result.CourseTargetResult;
+import com.qmth.distributed.print.business.entity.CourseTarget;
+import com.qmth.teachcloud.common.entity.SysUser;
+
+import java.util.List;
 
 /**
  * <p>
@@ -13,4 +19,38 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface CourseTargetService extends IService<CourseTarget> {
 
-}
+    /**
+     * 查询课程目标集合
+     *
+     * @param examId     考试id
+     * @param courseCode 课程编号
+     * @param userId     教师id
+     * @return 目标集合
+     */
+    List<CourseTargetResult> findCourseTargetList(Long examId, String courseCode, Long userId);
+
+    /**
+     * 查询知识点树
+     *
+     * @param examId     考试id
+     * @param courseCode 课程编号
+     * @param userId     教师id
+     * @return 课程知识点树
+     */
+    List<CourseDimensionTree> findDimensionTree(Long examId, String courseCode, Long userId);
+
+    /**
+     * 保存课程目标
+     *
+     * @param courseTargetParam 课程目标参数
+     * @param requestUser       请求人
+     */
+    void saveCourseTarget(CourseTargetParam courseTargetParam, SysUser requestUser);
+
+    /**
+     * 删除课程目标
+     *
+     * @param id 课程目标id
+     */
+    void deleteCourseTarget(Long id);
+}

+ 19 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/TeachCourseService.java

@@ -7,6 +7,7 @@ import com.qmth.distributed.print.business.bean.params.TeacherTeamParam;
 import com.qmth.distributed.print.business.bean.result.DictionaryResult;
 import com.qmth.distributed.print.business.bean.result.TeachCourseResult;
 import com.qmth.distributed.print.business.entity.TeachCourse;
+import com.qmth.distributed.print.business.enums.CourseSettingTypeEnum;
 import com.qmth.teachcloud.common.bean.result.TeacherSelectResult;
 import com.qmth.teachcloud.common.bean.result.TeacherTeamResult;
 import com.qmth.teachcloud.common.entity.BasicCourse;
@@ -24,7 +25,6 @@ import java.util.Set;
  */
 public interface TeachCourseService extends IService<TeachCourse> {
 
-
     /**
      * 分页查询教学课程列表
      *
@@ -130,4 +130,22 @@ public interface TeachCourseService extends IService<TeachCourse> {
      * @param teachIdSet
      */
     public void updateByCourseIdAndTeacherList(Long basicCourseId, Set<Long> teachIdSet);
+
+    /**
+     * 根据考试id,课程编号,教师id查询教学课程
+     *
+     * @param examId     考试id
+     * @param courseCode 课程编号
+     * @param userId     教师id
+     * @return 教学课程
+     */
+    TeachCourse findByExamIdCourseCodeAndUserId(Long examId, String courseCode, Long userId);
+
+    /**
+     * 清除课程目标、评价、权重设置
+     *
+     * @param teachCourseId         教学课程id
+     * @param courseSettingTypeEnum 课程设置类型
+     */
+    void clearCourseSetting(Long teachCourseId, CourseSettingTypeEnum courseSettingTypeEnum);
 }

+ 99 - 12
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/CourseDimensionServiceImpl.java

@@ -4,23 +4,33 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Lists;
 import com.qmth.boot.core.exception.StatusException;
-import com.qmth.distributed.print.business.bean.dto.CourseDimensionDto;
+import com.qmth.distributed.print.business.bean.dto.CourseDimensionImportDto;
 import com.qmth.distributed.print.business.entity.CourseDimension;
+import com.qmth.distributed.print.business.entity.TeachCourse;
 import com.qmth.distributed.print.business.enums.CourseDimensionCodeEnum;
+import com.qmth.distributed.print.business.enums.CourseDimensionSourceEnum;
+import com.qmth.distributed.print.business.enums.CourseSettingTypeEnum;
 import com.qmth.distributed.print.business.mapper.CourseDimensionMapper;
+import com.qmth.distributed.print.business.service.BasicExamService;
 import com.qmth.distributed.print.business.service.CourseDimensionService;
+import com.qmth.distributed.print.business.service.TeachCourseService;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.BasicExam;
 import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import com.qmth.teachcloud.common.util.ExcelUtil;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.web.multipart.MultipartFile;
 
+import javax.annotation.Resource;
 import java.io.IOException;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.stream.Collectors;
 
 /**
@@ -34,13 +44,33 @@ import java.util.stream.Collectors;
 @Service
 public class CourseDimensionServiceImpl extends ServiceImpl<CourseDimensionMapper, CourseDimension> implements CourseDimensionService {
 
+    @Resource
+    TeachCourseService teachCourseService;
+
+    @Resource
+    BasicExamService basicExamService;
+
     @Transactional
     @Override
     public void importCourseDimension(SysUser requestUser, MultipartFile file, Long examId, String courseCode)
             throws IOException, NoSuchFieldException {
+        Long requestUserId = requestUser.getId();
+        Long schoolId = requestUser.getSchoolId();
+
+        TeachCourse teachCourse = teachCourseService.findByExamIdCourseCodeAndUserId(examId, courseCode, requestUserId);
+        Long teachCourseId = teachCourse.getId();
+        // 删除课程设置
+        teachCourseService.clearCourseSetting(teachCourseId, CourseSettingTypeEnum.COURSE_DIMENSION);
+
+        BasicExam basicExam = basicExamService.getById(examId);
+        if (Objects.isNull(basicExam)) {
+            throw ExceptionResultEnum.ERROR.exception("考试不存在");
+        }
+        Long semesterId = SystemConstant.convertIdToLong(basicExam.getSemesterId());
+
         List<String> errorMsgList = new ArrayList<>();
-        List<CourseDimensionDto> list = new ArrayList<>();
-        ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(CourseDimensionDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
+        List<CourseDimensionImportDto> list = new ArrayList<>();
+        ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(CourseDimensionImportDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
             for (int i = 0; i < finalExcelList.size(); i++) {
                 LinkedMultiValueMap<Integer, Object> excelMap = finalExcelList.get(i);
                 List<Object> courseDimensionDtoList = excelMap.get(i);
@@ -52,11 +82,11 @@ public class CourseDimensionServiceImpl extends ServiceImpl<CourseDimensionMappe
 
                     // 行索引
                     Integer rowIndex = y + 1;
-                    CourseDimensionDto courseDimensionDto = (CourseDimensionDto) courseDimensionDtoList.get(y);
-                    String excelCourseCode = StringUtils.trimToNull(courseDimensionDto.getCourseCode());
-                    String excelCourseName = StringUtils.trimToNull(courseDimensionDto.getCourseName());
-                    String firstDimensionName = StringUtils.trimToNull(courseDimensionDto.getFirstDimensionName());
-                    String secondDimensionName = StringUtils.trimToNull(courseDimensionDto.getSecondDimensionName());
+                    CourseDimensionImportDto courseDimensionImportDto = (CourseDimensionImportDto) courseDimensionDtoList.get(y);
+                    String excelCourseCode = StringUtils.trimToNull(courseDimensionImportDto.getCourseCode());
+                    String excelCourseName = StringUtils.trimToNull(courseDimensionImportDto.getCourseName());
+                    String firstDimensionName = StringUtils.trimToNull(courseDimensionImportDto.getFirstDimensionName());
+                    String secondDimensionName = StringUtils.trimToNull(courseDimensionImportDto.getSecondDimensionName());
 
                     // 本行全部列为空,跳过
                     if (StringUtils.isAllBlank(excelCourseCode, excelCourseName, firstDimensionName, secondDimensionName)) {
@@ -65,17 +95,74 @@ public class CourseDimensionServiceImpl extends ServiceImpl<CourseDimensionMappe
                     if (courseCode.equals(excelCourseCode)) {
                         errorMsgList.add(String.format("第[%s]行,课程代码异常", rowIndex));
                     }
-                    list.add(courseDimensionDto);
+                    list.add(courseDimensionImportDto);
                 }
             }
             return finalExcelList;
         }, 2);
+
         if (CollectionUtils.isNotEmpty(errorMsgList)) {
             throw ExceptionResultEnum.ERROR.exception(String.join(";", errorMsgList));
-        } else {
-            // 处理要新增的考察点
-            // TODO: 2024/2/27  
         }
+
+        // 处理要新增的考察点
+        Map<String, List<String>> dimensionMap = new HashMap<>();
+        for (CourseDimensionImportDto courseDimensionImportDto : list) {
+            String firstDimensionName = courseDimensionImportDto.getFirstDimensionName();
+            String secondDimensionName = courseDimensionImportDto.getSecondDimensionName();
+
+            if (dimensionMap.containsKey(firstDimensionName)) {
+                List<String> sl = dimensionMap.get(firstDimensionName);
+                if (!sl.contains(secondDimensionName)) {
+                    sl.add(secondDimensionName);
+                    dimensionMap.put(firstDimensionName, sl);
+                }
+            } else {
+                List<String> sl = new ArrayList<>();
+                sl.add(secondDimensionName);
+                dimensionMap.put(firstDimensionName, sl);
+            }
+        }
+        CourseDimension temp = new CourseDimension();
+        temp.setTeachCourseId(teachCourseId);
+        temp.setSchoolId(schoolId);
+        temp.setSemesterId(semesterId);
+        temp.setExamId(examId);
+        temp.setCourseCode(courseCode);
+        temp.setUserId(requestUserId);
+        temp.setSource(CourseDimensionSourceEnum.IMPORT);
+
+        List<CourseDimension> willSaveDimensions = new ArrayList<>();
+
+        AtomicInteger firstNumber = new AtomicInteger(0);
+        for (String firstDimensionName : dimensionMap.keySet()) {
+            CourseDimension first = new CourseDimension();
+            int index = firstNumber.getAndIncrement();
+            BeanUtils.copyProperties(temp, first);
+            String firstCode = CourseDimensionCodeEnum.getCodeByIndex(index);
+            first.setCode(firstCode);
+            first.setName(firstDimensionName);
+            first.setNumber(index);
+            first.insertInfo(requestUserId);
+            first.setParentId(0L);
+            willSaveDimensions.add(first);
+
+            List<String> secondDimensionList = dimensionMap.get(firstDimensionName);
+            Long firstId = first.getId();
+            for (int i = 0; i < secondDimensionList.size(); i++) {
+                CourseDimension second = new CourseDimension();
+                int secondIndex = i + 1;
+                BeanUtils.copyProperties(temp, second);
+                String secondCode = firstCode + secondIndex;
+                second.setCode(secondCode);
+                second.setName(secondDimensionList.get(i));
+                second.setNumber(secondIndex);
+                second.insertInfo(requestUserId);
+                second.setParentId(firstId);
+                willSaveDimensions.add(second);
+            }
+        }
+        this.saveBatch(willSaveDimensions);
     }
 
     @Override

+ 206 - 1
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/CourseTargetServiceImpl.java

@@ -1,10 +1,32 @@
 package com.qmth.distributed.print.business.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.distributed.print.business.bean.dto.CourseDimensionDto;
+import com.qmth.distributed.print.business.bean.dto.CourseDimensionOccupiedDto;
+import com.qmth.distributed.print.business.bean.params.CourseTargetParam;
+import com.qmth.distributed.print.business.bean.result.CourseDimensionTree;
+import com.qmth.distributed.print.business.bean.result.CourseTargetResult;
+import com.qmth.distributed.print.business.entity.CourseDimension;
 import com.qmth.distributed.print.business.entity.CourseTarget;
+import com.qmth.distributed.print.business.entity.TeachCourse;
+import com.qmth.distributed.print.business.enums.CourseSettingTypeEnum;
 import com.qmth.distributed.print.business.mapper.CourseTargetMapper;
+import com.qmth.distributed.print.business.service.CourseDimensionService;
 import com.qmth.distributed.print.business.service.CourseTargetService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.distributed.print.business.service.TeachCourseService;
+import com.qmth.teachcloud.common.base.BaseEntity;
+import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.SysUser;
+import com.qmth.teachcloud.common.enums.ExceptionResultEnum;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * <p>
@@ -17,4 +39,187 @@ import org.springframework.stereotype.Service;
 @Service
 public class CourseTargetServiceImpl extends ServiceImpl<CourseTargetMapper, CourseTarget> implements CourseTargetService {
 
+    @Resource
+    private TeachCourseService teachCourseService;
+
+    @Resource
+    private CourseDimensionService courseDimensionService;
+
+    @Override
+    public List<CourseTargetResult> findCourseTargetList(Long examId, String courseCode, Long userId) {
+        TeachCourse teachCourse = teachCourseService.findByExamIdCourseCodeAndUserId(examId, courseCode, userId);
+        Long teachCourseId = teachCourse.getId();
+        List<CourseTarget> courseTargetList = this.list(
+                new QueryWrapper<CourseTarget>().lambda().eq(CourseTarget::getTeachCourseId, teachCourse.getId()));
+
+        List<CourseDimension> courseDimensionList = courseDimensionService.list(
+                new QueryWrapper<CourseDimension>().lambda().eq(CourseDimension::getTeachCourseId, teachCourseId));
+
+        Map<Long, CourseDimension> courseDimensionMap = courseDimensionList.stream()
+                .collect(Collectors.toMap(CourseDimension::getId, e -> e));
+
+        return courseTargetList.stream().flatMap(e -> {
+            CourseTargetResult cell = new CourseTargetResult();
+            cell.setId(e.getId());
+            cell.setTargetName(e.getTargetName());
+            cell.setDegreeRequirement(e.getDegreeRequirement());
+            String targetContent = e.getTargetContent();
+            List<Long> dimensionIdList = JSON.parseArray(targetContent, Long.class);
+            List<CourseDimensionDto> dimensionList = new ArrayList<>();
+
+            dimensionIdList.forEach(d -> {
+                CourseDimensionDto courseDimensionDto = new CourseDimensionDto();
+                CourseDimension courseDimension = courseDimensionMap.get(d);
+                courseDimensionDto.setId(d);
+                courseDimensionDto.setCode(courseDimension.getCode());
+                courseDimensionDto.setName(courseDimension.getName());
+                dimensionList.add(courseDimensionDto);
+            });
+            cell.setDimensionList(dimensionList);
+            return Stream.of(cell);
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public List<CourseDimensionTree> findDimensionTree(Long examId, String courseCode, Long userId) {
+        TeachCourse teachCourse = teachCourseService.findByExamIdCourseCodeAndUserId(examId, courseCode, userId);
+        Long teachCourseId = teachCourse.getId();
+
+        // 课程目标
+        List<CourseTarget> courseTargetList = this.list(
+                new QueryWrapper<CourseTarget>().lambda().eq(CourseTarget::getTeachCourseId, teachCourse.getId()));
+
+        // 课程目标id - 该目标包含的所有知识点id集合
+        Map<Long, List<Long>> targetDimensionMap = courseTargetList.stream()
+                .collect(Collectors.toMap(BaseEntity::getId, v -> {
+                    String targetContent = v.getTargetContent();
+                    return JSON.parseArray(targetContent, Long.class);
+                }));
+
+        // 课程知识点
+        List<CourseDimension> courseDimensionList = courseDimensionService.list(
+                new QueryWrapper<CourseDimension>().lambda().eq(CourseDimension::getTeachCourseId, teachCourseId));
+
+        List<CourseDimension> firstDimensionList = courseDimensionList.stream().filter(e -> e.getParentId() == 0).sorted(Comparator.comparingInt(CourseDimension::getNumber)).collect(Collectors.toList());
+
+        List<CourseDimensionTree> treeList = new ArrayList<>();
+        for (CourseDimension first : firstDimensionList) {
+            Long id = first.getId();
+            String code = first.getCode();
+            String name = first.getName();
+            CourseDimensionOccupiedDto occupied = this.buildCourseDimensionOccupiedDto(targetDimensionMap, id);
+            List<CourseDimensionTree> children = new ArrayList<>();
+
+            List<CourseDimension> secondDimensionList = courseDimensionList.stream().filter(e -> id.equals(e.getParentId())).sorted(Comparator.comparingInt(CourseDimension::getNumber))
+                    .collect(Collectors.toList());
+            for (CourseDimension child : secondDimensionList) {
+                Long childId = child.getId();
+                String childCode = child.getCode();
+                String childName = child.getName();
+                CourseDimensionOccupiedDto childOccupied = this.buildCourseDimensionOccupiedDto(targetDimensionMap,
+                        childId);
+
+                CourseDimensionTree tree = new CourseDimensionTree(childId, childCode, childName, childOccupied, new ArrayList<>());
+                children.add(tree);
+            }
+            CourseDimensionTree tree = new CourseDimensionTree(id, code, name, occupied, children);
+            treeList.add(tree);
+        }
+        return treeList;
+    }
+
+    @Transactional
+    @Override
+    public void saveCourseTarget(CourseTargetParam courseTargetParam, SysUser requestUser) {
+        Long requestUserId = requestUser.getId();
+        Long id = courseTargetParam.getId();
+        Long examId = courseTargetParam.getExamId();
+        String courseCode = courseTargetParam.getCourseCode();
+        String targetName = courseTargetParam.getTargetName();
+        String degreeRequirement = courseTargetParam.getDegreeRequirement();
+        List<Long> dimensionIdList = courseTargetParam.getDimensionIdList();
+        TeachCourse teachCourse = teachCourseService.findByExamIdCourseCodeAndUserId(examId, courseCode, requestUserId);
+        Long teachCourseId = teachCourse.getId();
+
+        // 知识点检查
+        Map<Long, CourseDimension> courseDimensionMap = courseDimensionService.list(
+                new QueryWrapper<CourseDimension>().lambda().eq(CourseDimension::getTeachCourseId, teachCourseId)).stream().collect(Collectors.toMap(BaseEntity::getId,
+                v -> v));
+
+        List<CourseTarget> courseTargetResultList = this.list(
+                new QueryWrapper<CourseTarget>().lambda().ne(CourseTarget::getId, id)
+                        .eq(CourseTarget::getTeachCourseId, teachCourseId));
+
+        // 占用的知识点id
+        List<Long> occupiedIdList = courseTargetResultList.stream().flatMap(e -> {
+            String content = e.getTargetContent();
+            List<Long> idList = JSON.parseArray(content, Long.class);
+            return idList.stream();
+        }).collect(Collectors.toList());
+        for (Long dimensionId : dimensionIdList) {
+            if (!courseDimensionMap.containsKey(dimensionId)) {
+                throw ExceptionResultEnum.ERROR.exception("知识点不存在");
+            }
+            if (occupiedIdList.contains(dimensionId)) {
+                throw ExceptionResultEnum.ERROR.exception(String.format("知识点[%s]已经被别的课程目标占用",
+                        courseDimensionMap.get(dimensionId).getName() + "(" + courseDimensionMap.get(dimensionId).getCode() + ")"));
+            }
+        }
+
+        teachCourseService.clearCourseSetting(teachCourseId, CourseSettingTypeEnum.COURSE_TARGET);
+
+        CourseTarget checkTargetName = this.getOne(
+                new QueryWrapper<CourseTarget>().lambda().eq(CourseTarget::getTeachCourseId, teachCourseId).eq(CourseTarget::getTargetName, targetName).last(SystemConstant.LIMIT1));
+        if (Objects.nonNull(checkTargetName) && !checkTargetName.getId().equals(id)) {
+            throw ExceptionResultEnum.ERROR.exception("课程目标名称重复");
+        }
+        CourseTarget courseTarget = new CourseTarget();
+        courseTarget.setTeachCourseId(teachCourseId);
+        courseTarget.setSchoolId(requestUser.getSchoolId());
+        courseTarget.setExamId(examId);
+        courseTarget.setCourseCode(courseCode);
+        courseTarget.setUserId(requestUserId);
+        courseTarget.setTargetName(targetName);
+        courseTarget.setDegreeRequirement(degreeRequirement);
+        courseTarget.setTargetContent(JSON.toJSONString(dimensionIdList));
+
+        if (SystemConstant.longNotNull(id)) {
+            // 编辑
+            courseTarget.updateInfo(requestUserId);
+        } else {
+            // 新增
+            courseTarget.insertInfo(requestUserId);
+        }
+        this.saveOrUpdate(courseTarget);
+    }
+
+    @Transactional
+    @Override
+    public void deleteCourseTarget(Long id) {
+        CourseTarget courseTarget = this.getById(id);
+        teachCourseService.clearCourseSetting(courseTarget.getTeachCourseId(), CourseSettingTypeEnum.COURSE_TARGET);
+        this.removeById(id);
+    }
+
+    /**
+     * 构建知识点占用情况对象
+     *
+     * @param targetDimensionMap 目标知识点map
+     * @param courseDimensionId  课程知识点id
+     * @return 占用情况
+     */
+    private CourseDimensionOccupiedDto buildCourseDimensionOccupiedDto(Map<Long, List<Long>> targetDimensionMap,
+            Long courseDimensionId) {
+        CourseDimensionOccupiedDto dto = new CourseDimensionOccupiedDto();
+        for (Long targetId : targetDimensionMap.keySet()) {
+            List<Long> dimensionList = targetDimensionMap.get(targetId);
+            if (dimensionList.contains(courseDimensionId)) {
+                dto.setCourseTargetId(targetId);
+                dto.setStatus(true);
+                return dto;
+            }
+        }
+        dto.setStatus(false);
+        return dto;
+    }
 }

+ 2 - 4
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/ExamTaskServiceImpl.java

@@ -2141,15 +2141,13 @@ public class ExamTaskServiceImpl extends ServiceImpl<ExamTaskMapper, ExamTask> i
 
     @Override
     public IPage<TikuPaperInfo> pageTikuPaper(String courseCode, String paperName, String account, Integer pageNumber, Integer pageSize) {
-//        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
-        Long schoolId = 2l;
+        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
         return tikuUtils.pagePaperInfo(schoolId, courseCode, paperName, account, pageNumber, pageSize);
     }
 
     @Override
     public String getTikuPaperData(Long paperId) {
-//        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
-        Long schoolId = 2l;
+        Long schoolId = Long.valueOf(ServletUtil.getRequestHeaderSchoolId().toString());
         tikuUtils.getTikuPaperData(schoolId, paperId);
         return null;
     }

+ 81 - 65
distributed-print-business/src/main/java/com/qmth/distributed/print/business/service/impl/TeachCourseServiceImpl.java

@@ -12,11 +12,11 @@ import com.qmth.distributed.print.business.bean.params.TeachCourseParams;
 import com.qmth.distributed.print.business.bean.params.TeacherTeamParam;
 import com.qmth.distributed.print.business.bean.result.DictionaryResult;
 import com.qmth.distributed.print.business.bean.result.TeachCourseResult;
-import com.qmth.distributed.print.business.entity.TeachClazz;
-import com.qmth.distributed.print.business.entity.TeachCourse;
+import com.qmth.distributed.print.business.entity.*;
+import com.qmth.distributed.print.business.enums.CourseEvaluationTypeEnum;
+import com.qmth.distributed.print.business.enums.CourseSettingTypeEnum;
 import com.qmth.distributed.print.business.mapper.TeachCourseMapper;
-import com.qmth.distributed.print.business.service.TeachClazzService;
-import com.qmth.distributed.print.business.service.TeachCourseService;
+import com.qmth.distributed.print.business.service.*;
 import com.qmth.teachcloud.common.base.BaseEntity;
 import com.qmth.teachcloud.common.bean.params.BasicCourseParams;
 import com.qmth.teachcloud.common.bean.params.UserSaveParams;
@@ -74,6 +74,18 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
     @Resource
     private TeachClazzService teachClazzService;
 
+    @Resource
+    private CourseTargetService courseTargetService;
+
+    @Resource
+    private CourseEvaluationService courseEvaluationService;
+
+    @Resource
+    private CourseWeightService courseWeightService;
+
+    @Resource
+    private CourseDimensionService courseDimensionService;
+
     @Override
     public IPage<TeachCourseResult> teachCoursePage(String courseName, int pageNumber, int pageSize, Long userId) {
         if (!SystemConstant.longNotNull(userId)) {
@@ -94,10 +106,8 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
         Long basicCourseId;
 
         // 校验
-        BasicCourse basicCourse = basicCourseService.getOne(new QueryWrapper<BasicCourse>()
-                .lambda()
-                .eq(BasicCourse::getSchoolId, schoolId)
-                .eq(BasicCourse::getCode, courseCode));
+        BasicCourse basicCourse = basicCourseService.getOne(
+                new QueryWrapper<BasicCourse>().lambda().eq(BasicCourse::getSchoolId, schoolId).eq(BasicCourse::getCode, courseCode));
         if (Objects.nonNull(basicCourse)) {
             // 根据课程编号查询存在课程判断名称是否一致
             if (!courseName.equals(basicCourse.getName())) {
@@ -105,11 +115,8 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
             } else {
                 basicCourseId = basicCourse.getId();
                 // 判断重复教学课程
-                if (this.count(new QueryWrapper<TeachCourse>()
-                        .lambda()
-                        .eq(TeachCourse::getSchoolId, schoolId)
-                        .eq(TeachCourse::getUserId, userId)
-                        .eq(TeachCourse::getBasicCourseId, basicCourseId)) > 0) {
+                if (this.count(new QueryWrapper<TeachCourse>().lambda().eq(TeachCourse::getSchoolId, schoolId)
+                        .eq(TeachCourse::getUserId, userId).eq(TeachCourse::getBasicCourseId, basicCourseId)) > 0) {
                     throw ExceptionResultEnum.ERROR.exception("课程[" + courseName + "]已经创建");
                 }
             }
@@ -136,21 +143,13 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
         Long schoolId = requestUser.getSchoolId();
         Long userId = requestUser.getId();
         if (CollectionUtils.isNotEmpty(basicCourseIdList)) {
-            basicCourseIdList = basicCourseService.listByIds(basicCourseIdList)
-                    .stream()
-                    .filter(BasicCourse::getEnable)
-                    .map(BaseEntity::getId)
-                    .distinct()
-                    .collect(Collectors.toList());
+            basicCourseIdList = basicCourseService.listByIds(basicCourseIdList).stream().filter(BasicCourse::getEnable)
+                    .map(BaseEntity::getId).distinct().collect(Collectors.toList());
 
             if (CollectionUtils.isNotEmpty(basicCourseIdList)) {
-                List<Long> alreadyCreated = this.list(new QueryWrapper<TeachCourse>().lambda()
-                                .eq(TeachCourse::getUserId, userId)
-                                .in(TeachCourse::getBasicCourseId, basicCourseIdList))
-                        .stream()
-                        .map(TeachCourse::getBasicCourseId)
-                        .distinct()
-                        .collect(Collectors.toList());
+                List<Long> alreadyCreated = this.list(
+                                new QueryWrapper<TeachCourse>().lambda().eq(TeachCourse::getUserId, userId).in(TeachCourse::getBasicCourseId, basicCourseIdList)).stream()
+                        .map(TeachCourse::getBasicCourseId).distinct().collect(Collectors.toList());
 
                 // 去掉已经创建的教学课程
                 basicCourseIdList.removeAll(alreadyCreated);
@@ -174,12 +173,13 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
     @Transactional
     @Override
     public void importTeachCourse(MultipartFile file, SysUser requestUser) throws IOException, NoSuchFieldException {
-        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(TeachCourseDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
-            if (finalExcelErrorList.size() > 0) {
-                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(finalExcelErrorList));
-            }
-            return finalExcelList;
-        },2);
+        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(TeachCourseDto.class),
+                (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
+                    if (finalExcelErrorList.size() > 0) {
+                        throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(finalExcelErrorList));
+                    }
+                    return finalExcelList;
+                }, 2);
 
         if (Objects.nonNull(finalList) && finalList.size() > 0) {
             for (int i = 0; i < finalList.size(); i++) {
@@ -226,8 +226,9 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
         // 删除课程时,同步删除班级、学生、教师
         List<TeachCourse> teachCourseList = this.listByIds(idList);
         for (TeachCourse teachCourse : teachCourseList) {
-            List<TeachClazz> teachClazzList = teachClazzService.listByBasicCourseIdAndUserId(teachCourse.getSchoolId(), teachCourse.getBasicCourseId(), teachCourse.getUserId());
-            if(CollectionUtils.isNotEmpty(teachClazzList)){
+            List<TeachClazz> teachClazzList = teachClazzService.listByBasicCourseIdAndUserId(teachCourse.getSchoolId(),
+                    teachCourse.getBasicCourseId(), teachCourse.getUserId());
+            if (CollectionUtils.isNotEmpty(teachClazzList)) {
                 List<Long> teachClazzIds = teachClazzList.stream().map(TeachClazz::getId).collect(Collectors.toList());
                 teachClazzService.deleteTeachClazzBatch(teachClazzIds);
             }
@@ -245,9 +246,8 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
         Long schoolId = requestUser.getSchoolId();
         Long teacherId;
 
-        SysUser dbUser = sysUserService.getOne(new QueryWrapper<SysUser>().lambda()
-                .eq(SysUser::getSchoolId, schoolId)
-                .eq(SysUser::getCode, code));
+        SysUser dbUser = sysUserService.getOne(
+                new QueryWrapper<SysUser>().lambda().eq(SysUser::getSchoolId, schoolId).eq(SysUser::getCode, code));
         if (Objects.nonNull(dbUser)) {
             if (!name.equals(dbUser.getRealName())) {
                 // 用户名称不一致
@@ -255,19 +255,15 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
             } else {
                 teacherId = dbUser.getId();
                 // 判断在该教师团队下已经存在该教师
-                if (this.count(new QueryWrapper<TeachCourse>()
-                        .lambda()
-                        .eq(TeachCourse::getSchoolId, schoolId)
-                        .eq(TeachCourse::getBasicCourseId, basicCourseId)
-                        .eq(TeachCourse::getUserId, teacherId)) > 0) {
+                if (this.count(new QueryWrapper<TeachCourse>().lambda().eq(TeachCourse::getSchoolId, schoolId)
+                        .eq(TeachCourse::getBasicCourseId, basicCourseId).eq(TeachCourse::getUserId, teacherId)) > 0) {
                     throw ExceptionResultEnum.ERROR.exception("教师[" + name + "]已经在教师团队中");
                 }
             }
         } else {
             // 不存在用户,直接创建一个学校老师用户
-            SysRole schoolTeacher = sysRoleService.getOne(new QueryWrapper<SysRole>()
-                    .lambda()
-                    .eq(SysRole::getType, RoleTypeEnum.SCHOOL_TEACHER));
+            SysRole schoolTeacher = sysRoleService.getOne(
+                    new QueryWrapper<SysRole>().lambda().eq(SysRole::getType, RoleTypeEnum.SCHOOL_TEACHER));
             if (Objects.isNull(schoolTeacher)) {
                 throw ExceptionResultEnum.ERROR.exception("缺少学校教师角色");
             }
@@ -280,7 +276,7 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
             userSaveParams.setPassword(SystemConstant.DEFAULT_PASSWORD);
             userSaveParams.setOrgId(requestUser.getOrgId());
             userSaveParams.setEnable(true);
-            userSaveParams.setRoleIds(new Long[]{schoolTeacher.getId()});
+            userSaveParams.setRoleIds(new Long[] { schoolTeacher.getId() });
             teacherId = sysUserService.saveUser(userSaveParams);
         }
         // 添加教师团队
@@ -300,20 +296,13 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
         Long requestUserId = requestUser.getId();
         if (CollectionUtils.isNotEmpty(userIdList)) {
             // 过滤userIdList
-            userIdList = sysUserService.listByIds(userIdList).stream()
-                    .filter(SysUser::getEnable)
-                    .map(BaseEntity::getId)
-                    .distinct()
-                    .collect(Collectors.toList());
+            userIdList = sysUserService.listByIds(userIdList).stream().filter(SysUser::getEnable).map(BaseEntity::getId)
+                    .distinct().collect(Collectors.toList());
 
             if (CollectionUtils.isNotEmpty(userIdList)) {
-                List<Long> alreadyCreated = this.list(new QueryWrapper<TeachCourse>().lambda()
-                                .in(TeachCourse::getUserId, userIdList)
-                                .eq(TeachCourse::getBasicCourseId, basicCourseId))
-                        .stream()
-                        .map(TeachCourse::getUserId)
-                        .distinct()
-                        .collect(Collectors.toList());
+                List<Long> alreadyCreated = this.list(
+                                new QueryWrapper<TeachCourse>().lambda().in(TeachCourse::getUserId, userIdList).eq(TeachCourse::getBasicCourseId, basicCourseId)).stream().map(TeachCourse::getUserId)
+                        .distinct().collect(Collectors.toList());
 
                 // 去掉已经在该课程教师团队的老师
                 userIdList.removeAll(alreadyCreated);
@@ -338,12 +327,13 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
     @Transactional
     @Override
     public void importTeacherTeam(MultipartFile file, Long basicCourseId, SysUser requestUser) throws IOException, NoSuchFieldException, IllegalAccessException {
-        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(TeacherTeamDto.class), (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
-            if (finalExcelErrorList.size() > 0) {
-                throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(finalExcelErrorList));
-            }
-            return finalExcelList;
-        },2);
+        List<LinkedMultiValueMap<Integer, Object>> finalList = ExcelUtil.excelReader(file.getInputStream(), Lists.newArrayList(TeacherTeamDto.class),
+                (finalExcelList, finalColumnNameList, finalExcelErrorList) -> {
+                    if (finalExcelErrorList.size() > 0) {
+                        throw ExceptionResultEnum.ERROR.exception(JSONObject.toJSONString(finalExcelErrorList));
+                    }
+                    return finalExcelList;
+                }, 2);
 
         if (Objects.nonNull(finalList) && finalList.size() > 0) {
             for (int i = 0; i < finalList.size(); i++) {
@@ -369,7 +359,8 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
         if (SystemConstant.strNotNull(userInfo)) {
             userInfo = SystemConstant.translateSpecificSign(userInfo);
         }
-        IPage<TeacherTeamResult> page = this.baseMapper.findTeacherTeamPage(new Page<>(pageNumber, pageSize), userInfo, basicCourseId, schoolId);
+        IPage<TeacherTeamResult> page = this.baseMapper.findTeacherTeamPage(new Page<>(pageNumber, pageSize), userInfo,
+                basicCourseId, schoolId);
         page.getRecords().forEach(e -> {
             Long userId = e.getUserId();
             List<Long> userIdList = new ArrayList<>();
@@ -417,4 +408,29 @@ public class TeachCourseServiceImpl extends ServiceImpl<TeachCourseMapper, Teach
             this.saveBatch(teachCourseList);
         }
     }
+
+    @Override
+    public TeachCourse findByExamIdCourseCodeAndUserId(Long examId, String courseCode, Long userId) {
+        TeachCourse teachCourse = this.getOne(
+                new QueryWrapper<TeachCourse>().lambda().eq(TeachCourse::getExamId, examId).eq(TeachCourse::getCourseCode, courseCode).eq(TeachCourse::getUserId, userId)
+                        .last(SystemConstant.LIMIT1));
+        if (Objects.isNull(teachCourse)) {
+            throw ExceptionResultEnum.ERROR.exception("教学课程不存在");
+        }
+        return teachCourse;
+    }
+
+    @Transactional
+    @Override
+    public void clearCourseSetting(Long teachCourseId, CourseSettingTypeEnum courseSettingTypeEnum) {
+        if (courseSettingTypeEnum == CourseSettingTypeEnum.COURSE_DIMENSION) {// 删除知识点、目标设置
+            courseDimensionService.remove(
+                    new QueryWrapper<CourseDimension>().lambda().eq(CourseDimension::getTeachCourseId, teachCourseId));
+            courseTargetService.remove(
+                    new QueryWrapper<CourseTarget>().lambda().eq(CourseTarget::getTeachCourseId, teachCourseId));
+        }
+        // 删除权重设置
+        courseWeightService.remove(
+                new QueryWrapper<CourseWeight>().lambda().eq(CourseWeight::getTeachCourseId, teachCourseId));
+    }
 }

+ 38 - 1
distributed-print-business/src/main/resources/db/log/脚本-caozx.sql

@@ -15,6 +15,43 @@ CREATE TABLE `course_dimension` (
     PRIMARY KEY (`id`)
 )  ENGINE=INNODB DEFAULT CHARACTER SET=UTF8MB4 COMMENT='课程知识点表';
 
+ALTER TABLE `teach_course` 
+ADD COLUMN `weight_setting` TINYINT(1) NULL COMMENT '权重设置' AFTER `enable`,
+ADD COLUMN `exam_id` BIGINT NOT NULL COMMENT '考试id' AFTER `school_id`,
+ADD COLUMN `course_code` VARCHAR(20) NOT NULL COMMENT '课程编号' AFTER 'exam_id',
+CHANGE COLUMN `user_id` `user_id` BIGINT NOT NULL COMMENT '任课教师id' ,
+DROP INDEX `teach_course_unique` ,
+ADD UNIQUE INDEX `teach_course_unique` (`school_id` ASC, `exam_id` ASC, `course_code` ASC, `user_id` ASC) INVISIBLE;
+;
+
+ALTER TABLE `course_dimension` 
+ADD COLUMN `teach_course_id` BIGINT NOT NULL COMMENT '教学课程id' AFTER `id`,
+ADD COLUMN `user_id` BIGINT NOT NULL COMMENT '教师id' AFTER `course_code`,
+ADD COLUMN `source` VARCHAR(20) NOT NULL COMMENT '来源' AFTER `user_id`;
 ALTER TABLE `course_dimension` 
-ADD UNIQUE INDEX `course_dimension_unique` (`exam_id` ASC, `course_code` ASC, `code` ASC) VISIBLE;
+DROP INDEX `course_dimension_unique` ,
+ADD UNIQUE INDEX `course_dimension_unique` (`teach_course_id` ASC, `source` ASC, `code` ASC) INVISIBLE;
+;
+
+ALTER TABLE `course_evaluation` 
+ADD COLUMN `teach_course_id` BIGINT NOT NULL COMMENT '教学课程id' AFTER `id`,
+ADD COLUMN `user_id` BIGINT NOT NULL COMMENT '教师id' AFTER `course_code`,
+ADD COLUMN `type` VARCHAR(20) NOT NULL COMMENT '类型(默认DEFAULT、自定义CUSTOM)' AFTER `user_id`,
+DROP INDEX `course_evaluation_unique` ,
+ADD UNIQUE INDEX `course_evaluation_unique` (`teach_course_id` ASC, `evaluation` ASC) VISIBLE;
 ;
+
+ALTER TABLE `course_target` 
+ADD COLUMN `teach_course_id` BIGINT NOT NULL COMMENT '教学课程id' AFTER `id`,
+ADD COLUMN `user_id` BIGINT NOT NULL COMMENT '教师id' AFTER `course_code`,
+DROP INDEX `course_target_unique` ,
+ADD UNIQUE INDEX `course_target_unique` (`teach_course_id` ASC, `target_name` ASC) VISIBLE;
+;
+
+ALTER TABLE `course_weight` 
+ADD COLUMN `teach_course_id` BIGINT NOT NULL COMMENT '教学课程id' AFTER `id`,
+ADD COLUMN `user_id` BIGINT NOT NULL COMMENT '教师id' AFTER `course_code`,
+DROP INDEX `course_weight_unique` ,
+ADD UNIQUE INDEX `course_weight_unique` (`teach_course_id` ASC, `target_id` ASC, `evaluation_id` ASC) INVISIBLE;
+;
+

+ 6 - 0
distributed-print/install/mysql/upgrade/3.3.1.sql

@@ -395,3 +395,9 @@ ALTER TABLE `exam_card` ADD COLUMN `blank_card_path` VARCHAR(500) NULL COMMENT '
 
 INSERT INTO `sys_privilege` (`id`, `name`, `url`, `type`, `parent_id`, `sequence`, `property`, `enable`, `default_auth`, `front_display`) VALUES ('241', '印刷-单文件打印', '/api/admin/client/print/single', 'URL', '199', '17', 'AUTH', '1', '1', '1');
 UPDATE `sys_privilege` SET `related` = '200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,241,973,974,975' WHERE (`id` = '199');
+
+-- 2024/02/29
+INSERT INTO `sys_privilege` (`id`, `name`, `url`, `type`, `parent_id`, `sequence`, `property`, `enable`, `default_auth`, `front_display`) VALUES ('286', '题库试卷列表', '/api/admin/exam/task/page_tiku_paper', 'URL', '40', '12', 'AUTH', '1', '1', '1');
+INSERT INTO `sys_privilege` (`id`, `name`, `url`, `type`, `parent_id`, `sequence`, `property`, `enable`, `default_auth`, `front_display`) VALUES ('287', '题库试卷包', '/api/admin/exam/task/get_tiku_paper_data', 'URL', '40', '13', 'AUTH', '1', '1', '1');
+UPDATE `sys_privilege` SET `related` = '71,167,286,287,424,477,417,420,421,441,442,640,641' WHERE (`id` = '283');
+UPDATE `sys_privilege` SET `related` = '49,167,174,286,287,371,464,465,427,642,417,420,421,441,442,640,641,642' WHERE (`id` = '463');

+ 1 - 1
distributed-print/src/main/java/com/qmth/distributed/print/api/BasicExamStudentController.java

@@ -111,7 +111,7 @@ public class BasicExamStudentController {
     @RequestMapping(value = "/delete", method = RequestMethod.POST)
     @ApiResponses({ @ApiResponse(code = 200, message = "删除成功", response = Result.class) })
     @OperationLogDetail(customizedOperationType = CustomizedOperationTypeEnum.DELETE)
-    public Result deleteBasicExamStudent(@ApiParam(value = "选择要删除的考生id集合", required = true) @RequestParam List<String> idList) {
+    public Result deleteBasicExamStudent(@ApiParam(value = "选择要删除的考生id集合", required = true) @RequestParam List<String> idList) {
         basicExamStudentService.deleteBatch(
                 idList.stream().map(SystemConstant::convertIdToLong).collect(Collectors.toList()));
         return ResultUtil.ok();

+ 68 - 10
distributed-print/src/main/java/com/qmth/distributed/print/api/CourseTargetController.java

@@ -1,19 +1,26 @@
 package com.qmth.distributed.print.api;
 
 import com.qmth.boot.api.constant.ApiConstant;
-import com.qmth.distributed.print.business.service.PrintCommonService;
+import com.qmth.distributed.print.business.bean.params.CourseTargetParam;
+import com.qmth.distributed.print.business.bean.result.CourseDimensionTree;
+import com.qmth.distributed.print.business.bean.result.CourseTargetResult;
+import com.qmth.distributed.print.business.service.CourseDimensionService;
+import com.qmth.distributed.print.business.service.CourseTargetService;
 import com.qmth.teachcloud.common.annotation.OperationLogDetail;
 import com.qmth.teachcloud.common.contant.SystemConstant;
+import com.qmth.teachcloud.common.entity.SysUser;
 import com.qmth.teachcloud.common.enums.log.CustomizedOperationTypeEnum;
 import com.qmth.teachcloud.common.util.Result;
+import com.qmth.teachcloud.common.util.ResultUtil;
+import com.qmth.teachcloud.common.util.ServletUtil;
 import io.swagger.annotations.*;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.io.IOException;
 
 /**
  * <p>
@@ -25,17 +32,68 @@ import javax.annotation.Resource;
  */
 @Api(tags = "课程目标管理controller")
 @RestController
-@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + SystemConstant.PREFIX_URL_TEACH + "/course-target")
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + SystemConstant.PREFIX_URL_TEACH + "/course_target")
 public class CourseTargetController {
+
+    @Resource
+    private CourseTargetService courseTargetService;
+
     @Resource
-    private PrintCommonService printCommonService;
+    private CourseDimensionService courseDimensionService;
+
+    @ApiOperation(value = "课程目标管理-列表查询")
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    @ApiResponses({ @ApiResponse(code = 200, message = "查询成功", response = CourseTargetResult.class) })
+    public Result findCourseTargetList(@ApiParam(value = "学期id") @RequestParam(required = false) String examId,
+            @ApiParam(value = "考试id") @RequestParam(required = false) String courseCode) {
+        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
+        return ResultUtil.ok(
+                courseTargetService.findCourseTargetList(SystemConstant.convertIdToLong(examId), courseCode, requestUser.getId()));
+    }
 
     @ApiOperation(value = "课程目标管理-导入课程知识点")
     @RequestMapping(value = "/dimension_import", method = RequestMethod.POST)
     @ApiResponses({ @ApiResponse(code = 200, message = "返回信息", response = Result.class) })
     @OperationLogDetail(customizedOperationType = CustomizedOperationTypeEnum.IMPORT)
-    public Result courseDimensionImportSync(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file) {
+    public Result courseDimensionImportSync(@ApiParam(value = "上传文件", required = true) @RequestParam MultipartFile file,
+            @ApiParam(value = "考试id", required = true) @RequestParam String examId,
+            @ApiParam(value = "课程编号", required = true) @RequestParam String courseCode)
+            throws IOException, NoSuchFieldException {
+        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
+        courseDimensionService.importCourseDimension(requestUser, file, SystemConstant.convertIdToLong(examId),
+                courseCode);
+        return ResultUtil.ok();
+    }
+
+    @ApiOperation(value = "课程目标管理-课程知识点树查询")
+    @RequestMapping(value = "/dimension_tree", method = RequestMethod.POST)
+    @ApiResponses({ @ApiResponse(code = 200, message = "查询成功", response = CourseDimensionTree.class) })
+    public Result findDimensionTree(@ApiParam(value = "学期id") @RequestParam(required = false) String examId,
+            @ApiParam(value = "考试id") @RequestParam(required = false) String courseCode) {
+        SysUser requestUser = (SysUser) ServletUtil.getRequestUser();
+        return ResultUtil.ok(courseTargetService.findDimensionTree(SystemConstant.convertIdToLong(examId), courseCode,
+                requestUser.getId()));
+    }
+
+    @ApiOperation(value = "课程目标管理-新增/编辑")
+    @RequestMapping(value = "/save", method = RequestMethod.POST)
+    @ApiResponses({ @ApiResponse(code = 200, message = "更新成功", response = Result.class) })
+    @OperationLogDetail(customizedOperationType = CustomizedOperationTypeEnum.EDIT)
+    public Result saveCourseTarget(@Valid @RequestBody CourseTargetParam courseTargetParam, BindingResult bindingResult) {
+        if (bindingResult.hasErrors()) {
+            return ResultUtil.error(bindingResult.getAllErrors().get(0).getDefaultMessage());
+        }
+        SysUser sysUser = (SysUser) ServletUtil.getRequestUser();
+        courseTargetService.saveCourseTarget(courseTargetParam, sysUser);
+        return ResultUtil.ok();
+    }
 
-        return null;
+    @ApiOperation(value = "课程目标管理-删除")
+    @RequestMapping(value = "/delete", method = RequestMethod.POST)
+    @ApiResponses({ @ApiResponse(code = 200, message = "删除成功", response = Result.class) })
+    @OperationLogDetail(customizedOperationType = CustomizedOperationTypeEnum.DELETE)
+    public Result deleteCourseTarget(@ApiParam(value = "选择要删除的课程目标id", required = true) @RequestParam Long id) {
+        courseTargetService.deleteCourseTarget(id);
+        return ResultUtil.ok();
     }
-}
+}

+ 0 - 2
distributed-print/src/main/java/com/qmth/distributed/print/api/ExamTaskApplyController.java

@@ -262,7 +262,6 @@ public class ExamTaskApplyController {
         return ResultUtil.ok(examTaskService.examTaskExamStudentImport(file, requestUser));
     }
 
-    @Aac(auth = false)
     @ApiOperation(value = "入库申请-题库试卷列表")
     @RequestMapping(value = "/page_tiku_paper", method = RequestMethod.POST)
     @Transactional(rollbackFor = Exception.class)
@@ -275,7 +274,6 @@ public class ExamTaskApplyController {
         return ResultUtil.ok(examTaskService.pageTikuPaper(courseCode, paperName, account, pageNumber, pageSize));
     }
 
-    @Aac(auth = false)
     @ApiOperation(value = "入库申请-题库试卷包")
     @RequestMapping(value = "/get_tiku_paper_data", method = RequestMethod.POST)
     @Transactional(rollbackFor = Exception.class)

+ 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);
         }