refactor(exam): 优化考试单词生成逻辑并新增期中期末类型
- 调整考试类型选择,增加“期中”和“期末”选项 - 删除旧的gradeId和level参数,简化接口参数为studentId和type - 新增考试类型常量:期中(2)、期末(3) - 实现期中考试和期末考试生成逻辑,分别根据年级及单元名称筛选词汇 - 调整服务层方法签名及调用,支持新考试类型生成流程 - 扩展Mapper接口,支持按单元名称和单元ID查询词汇 - 优化导出逻辑,导出文件名和压缩包名称根据考试标题动态生成 - 调整测试代码,适配新的方法参数和实现细节
This commit is contained in:
@@ -32,6 +32,8 @@ public class ExamWordsConstant {
|
||||
|
||||
|
||||
public static final int EXAM_TYPE_BASELINE = 1;
|
||||
public static final int EXAM_TYPE_MIDTERM = 2;
|
||||
public static final int EXAM_TYPE_FINAL = 3;
|
||||
|
||||
public static int getZoneA(int gradeId) {
|
||||
return switch (gradeId) {
|
||||
|
||||
@@ -42,15 +42,13 @@ public class ExamWordsController {
|
||||
|
||||
@PostMapping("generate")
|
||||
public void generateFeltExamWords(@RequestBody GenerateExamWordsReqVO generateExamWordsReqVO, HttpServletResponse response) {
|
||||
Integer gradeId = generateExamWordsReqVO.getGradeId();
|
||||
Integer level = generateExamWordsReqVO.getLevel();
|
||||
Integer type = generateExamWordsReqVO.getType();
|
||||
Integer studentId = generateExamWordsReqVO.getStudentId();
|
||||
if (studentId == null || gradeId == null || level == null) {
|
||||
if (studentId == null) {
|
||||
throw new RuntimeException("参数错误");
|
||||
}
|
||||
try {
|
||||
ExamWordsDO examWordsDO = examWordsService.generateExamWords(gradeId, level, studentId, type);
|
||||
ExamWordsDO examWordsDO = examWordsService.generateExamWords(studentId, type);
|
||||
if (examWordsDO == null || examWordsDO.getWordIds().isEmpty()) {
|
||||
throw new RuntimeException("没有单词");
|
||||
}
|
||||
@@ -77,7 +75,7 @@ public class ExamWordsController {
|
||||
return data;
|
||||
}).toList();
|
||||
|
||||
WordExportUtil.generateExamWords(maps, response, templateWordPath);
|
||||
WordExportUtil.generateExamWords(maps, examWordsDO, response, templateWordPath);
|
||||
|
||||
} catch (Exception e) {
|
||||
|
||||
|
||||
@@ -25,4 +25,6 @@ public interface UnitDOMapper {
|
||||
List<UnitDO> selectUnitDOList(@Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize);
|
||||
|
||||
Integer selectUnitDOListCount();
|
||||
|
||||
List<UnitDO> selectByUnitName(@Param("unitName") String unitName);
|
||||
}
|
||||
@@ -24,4 +24,6 @@ public interface VocabularyBankDOMapper {
|
||||
List<VocabularyBankDO> selectVocabularyBankListByGradeIdRandom(@Param("gradeId") Integer gradeId, @Param("wordCount") Integer wordCount);
|
||||
|
||||
Integer selectWordTotal();
|
||||
|
||||
List<VocabularyBankDO> selectByUnitIds(@Param("unitIds") List<Integer> unitIds);
|
||||
}
|
||||
@@ -14,8 +14,6 @@ import java.util.List;
|
||||
@Builder
|
||||
public class GenerateExamWordsReqVO {
|
||||
|
||||
private Integer gradeId;
|
||||
private Integer level;
|
||||
private Integer type;
|
||||
private Integer studentId;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.List;
|
||||
|
||||
public interface ExamWordsService {
|
||||
|
||||
ExamWordsDO generateExamWords(Integer gradeId, Integer level, Integer studentId, Integer type);
|
||||
ExamWordsDO generateExamWords(Integer studentId, Integer type);
|
||||
|
||||
int saveExamWordsPngToDbAndLocal(MultipartFile file);
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package com.yinlihupo.enlish.service.service.exam;
|
||||
|
||||
import com.yinlihupo.enlish.service.constant.ExamWordsConstant;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsDO;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsJudgeResultDO;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentDO;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.*;
|
||||
import com.yinlihupo.enlish.service.domain.mapper.*;
|
||||
import com.yinlihupo.enlish.service.service.ExamWordsService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -16,6 +14,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -26,8 +25,6 @@ import java.util.UUID;
|
||||
@Slf4j
|
||||
public class ExamWordsServiceImpl implements ExamWordsService {
|
||||
|
||||
@Resource
|
||||
private GradeUnitDOMapper gradeUnitDOMapper;
|
||||
@Resource
|
||||
private VocabularyBankDOMapper vocabularyBankDOMapper;
|
||||
@Resource
|
||||
@@ -38,6 +35,8 @@ public class ExamWordsServiceImpl implements ExamWordsService {
|
||||
private ExamWordsJudgeResultDOMapper examWordsJudgeResultDOMapper;
|
||||
@Resource
|
||||
private StudentDOMapper studentDOMapper;
|
||||
@Resource
|
||||
private UnitDOMapper unitDOMapper;
|
||||
|
||||
@Value("${templates.count}")
|
||||
private Integer wordCount;
|
||||
@@ -46,7 +45,7 @@ public class ExamWordsServiceImpl implements ExamWordsService {
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = RuntimeException.class)
|
||||
public ExamWordsDO generateExamWords(Integer gradeId, Integer level, Integer studentId, Integer type) {
|
||||
public ExamWordsDO generateExamWords(Integer studentId, Integer type) {
|
||||
|
||||
|
||||
ExamWordsDO examWordsDO;
|
||||
@@ -54,9 +53,10 @@ public class ExamWordsServiceImpl implements ExamWordsService {
|
||||
if (type == ExamWordsConstant.EXAM_TYPE_BASELINE) {
|
||||
log.info("生成摸底测试");
|
||||
examWordsDO = generateBaselineExamWords(studentId);
|
||||
} else if (type == ExamWordsConstant.EXAM_TYPE_MIDTERM) {
|
||||
examWordsDO = generateMidtermExamWords(studentId);
|
||||
} else {
|
||||
// todo 生成期中考试待实现
|
||||
examWordsDO = null;
|
||||
examWordsDO = generateFinalExamWords(studentId);
|
||||
}
|
||||
|
||||
return examWordsDO;
|
||||
@@ -95,11 +95,54 @@ public class ExamWordsServiceImpl implements ExamWordsService {
|
||||
.gradeId(gradeId)
|
||||
.level(1)
|
||||
.type(ExamWordsConstant.EXAM_TYPE_BASELINE)
|
||||
.title("摸低测试测试" + studentDO.getName())
|
||||
.title("摸低测试" + studentDO.getName())
|
||||
.createdAt(LocalDateTime.now())
|
||||
.wordIds(vocabularyBankDOS.stream().map(VocabularyBankDO::getId).toList())
|
||||
.build();
|
||||
|
||||
return getExamWordsDO(studentId, examWordsDO);
|
||||
}
|
||||
|
||||
private ExamWordsDO generateMidtermExamWords(Integer studentId) {
|
||||
StudentDO studentDO = studentDOMapper.selectStudentById(studentId);
|
||||
Integer gradeId = studentDO.getGradeId();
|
||||
List<UnitDO> unitDOS = unitDOMapper.selectByUnitName(ExamWordsConstant.getGradeName(gradeId) + "上");
|
||||
ExamWordsDO examWordsDO = getExamWordsDO(studentId, studentDO, gradeId, unitDOS);
|
||||
examWordsDO.setTitle("期中测试" + studentDO.getName());
|
||||
return getExamWordsDO(studentId, examWordsDO);
|
||||
|
||||
}
|
||||
|
||||
private ExamWordsDO generateFinalExamWords(Integer studentId) {
|
||||
StudentDO studentDO = studentDOMapper.selectStudentById(studentId);
|
||||
Integer gradeId = studentDO.getGradeId();
|
||||
List<UnitDO> unitDOS = unitDOMapper.selectByUnitName(ExamWordsConstant.getGradeName(gradeId));
|
||||
ExamWordsDO examWordsDO = getExamWordsDO(studentId, studentDO, gradeId, unitDOS);
|
||||
examWordsDO.setTitle("期末测试" + studentDO.getName());
|
||||
return getExamWordsDO(studentId, examWordsDO);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ExamWordsDO getExamWordsDO(Integer studentId, StudentDO studentDO, Integer gradeId, List<UnitDO> unitDOS) {
|
||||
if (unitDOS.isEmpty()) {
|
||||
throw new RuntimeException("没有找到对应的单元");
|
||||
}
|
||||
List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankDOMapper.selectByUnitIds(unitDOS.stream().map(UnitDO::getId).toList());
|
||||
|
||||
ExamWordsDO examWordsDO = ExamWordsDO.builder()
|
||||
.gradeId(gradeId)
|
||||
.level(1)
|
||||
.type(ExamWordsConstant.EXAM_TYPE_BASELINE)
|
||||
.title(studentDO.getName())
|
||||
.createdAt(LocalDateTime.now())
|
||||
.wordIds(vocabularyBankDOS.stream().map(VocabularyBankDO::getId).toList())
|
||||
.build();
|
||||
|
||||
return getExamWordsDO(studentId, examWordsDO);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ExamWordsDO getExamWordsDO(Integer studentId, ExamWordsDO examWordsDO) {
|
||||
int insert = examWordsDOMapper.insert(examWordsDO);
|
||||
if (insert <= 0) {
|
||||
throw new RuntimeException("插入考试失败");
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.yinlihupo.enlish.service.utils;
|
||||
import com.deepoove.poi.XWPFTemplate;
|
||||
import com.deepoove.poi.config.Configure;
|
||||
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsDO;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
import java.io.*;
|
||||
@@ -48,7 +49,7 @@ public class WordExportUtil {
|
||||
/**
|
||||
* 公共入口:根据数据量决定是导出单文件还是压缩包
|
||||
*/
|
||||
public static void generateExamWords(List<Map<String, Object>> data, HttpServletResponse response, String templateWordPath) {
|
||||
public static void generateExamWords(List<Map<String, Object>> data, ExamWordsDO examWordsDO, HttpServletResponse response, String templateWordPath) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -56,10 +57,10 @@ public class WordExportUtil {
|
||||
try {
|
||||
if (data.size() == 1) {
|
||||
// 如果只有一份数据,直接导出 docx,用户体验更好
|
||||
generateExamWordsDocx(data.get(0), response, templateWordPath);
|
||||
generateExamWordsDocx(data.get(0), examWordsDO, response, templateWordPath);
|
||||
} else {
|
||||
// 如果有多份数据,打包导出
|
||||
generateExamWordsZip(data, response, templateWordPath);
|
||||
generateExamWordsZip(data, examWordsDO, response, templateWordPath);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@@ -92,9 +93,9 @@ public class WordExportUtil {
|
||||
/**
|
||||
* 核心补充:批量渲染并打包为 ZIP
|
||||
*/
|
||||
private static void generateExamWordsZip(List<Map<String, Object>> data, HttpServletResponse response, String templateWordPath) throws IOException {
|
||||
private static void generateExamWordsZip(List<Map<String, Object>> data, ExamWordsDO examWordsDO, HttpServletResponse response, String templateWordPath) throws IOException {
|
||||
// 1. 设置响应头为 ZIP
|
||||
String zipName = URLEncoder.encode("批量导出_摸底测试.zip", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
|
||||
String zipName = URLEncoder.encode("批量导出_" + examWordsDO.getTitle() + ".zip", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + zipName);
|
||||
|
||||
@@ -106,7 +107,7 @@ public class WordExportUtil {
|
||||
for (Map<String, Object> itemData : data) {
|
||||
// 3. 确定压缩包内的文件名
|
||||
// 优先从 map 中获取 'fileName' 字段,否则使用默认编号
|
||||
String entryName = (String) itemData.getOrDefault("fileName", "摸底测试_" + index);
|
||||
String entryName = (String) itemData.getOrDefault("fileName", + index);
|
||||
// 确保文件名后缀正确
|
||||
if (!entryName.endsWith(".docx")) {
|
||||
entryName += ".docx";
|
||||
@@ -143,9 +144,9 @@ public class WordExportUtil {
|
||||
/**
|
||||
* 现有的单文件导出逻辑
|
||||
*/
|
||||
private static void generateExamWordsDocx(Map<String, Object> data, HttpServletResponse response, String templateWordPath) throws IOException {
|
||||
private static void generateExamWordsDocx(Map<String, Object> data, ExamWordsDO examWordsDO, HttpServletResponse response, String templateWordPath) throws IOException {
|
||||
|
||||
String fileName = URLEncoder.encode("摸底测试" + ".docx", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
|
||||
String fileName = URLEncoder.encode(examWordsDO.getTitle() + ".docx", StandardCharsets.UTF_8).replaceAll("\\+", "%20");
|
||||
|
||||
// 3. 设置响应头
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
|
||||
|
||||
@@ -101,5 +101,10 @@
|
||||
select count(*)
|
||||
from unit
|
||||
</select>
|
||||
<select id="selectByUnitName" resultType="com.yinlihupo.enlish.service.domain.dataobject.UnitDO">
|
||||
select *
|
||||
from unit
|
||||
where title like concat(#{unitName}, '%')
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -135,4 +135,15 @@
|
||||
limit #{wordCount}
|
||||
</select>
|
||||
|
||||
<select id="selectByUnitIds" resultMap="BaseResultMap">
|
||||
select *
|
||||
from vocabulary_bank
|
||||
where unit_id in
|
||||
<foreach item="unitId" collection="unitIds" separator="," open="(" close=")">
|
||||
#{unitId}
|
||||
</foreach>
|
||||
order by rand()
|
||||
limit 100
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -26,7 +26,7 @@ public class ExamTest {
|
||||
private VocabularyService vocabularyService;
|
||||
@Test
|
||||
public void test() {
|
||||
ExamWordsDO examWordsDO = examWordsService.generateExamWords(5, 0, 1, 0);
|
||||
ExamWordsDO examWordsDO = examWordsService.generateExamWords(5, 0);
|
||||
log.info("{}", examWordsDO);
|
||||
List<VocabularyBankDO> vocabularyBankDOS = vocabularyService.findVocabularyBankDOListById(examWordsDO.getWordIds());
|
||||
List<Word> assessmentWords = vocabularyBankDOS.stream().map(vocabularyBankDO -> Word.builder()
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
<el-form-item label="类型">
|
||||
<el-select v-model="type" placeholder="请选择类型" style="width: 240px">
|
||||
<el-option :label="'摸底'" :value="1" />
|
||||
<el-option :label="'期中|期末'" :value="2" />
|
||||
<el-option :label="'期中'" :value="2" />
|
||||
<el-option :label="'期末'" :value="3" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -61,8 +62,6 @@ async function fetchGrades() {
|
||||
async function handleGenerate() {
|
||||
if (!type.value) return
|
||||
await generateExamWords({
|
||||
gradeId: Number(gradeId.value),
|
||||
level: Number(level.value),
|
||||
type: Number(type.value),
|
||||
studentId: props.studentIds[0]
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user