From e0258c7ddf587964aed059f713a29fdc8229d098 Mon Sep 17 00:00:00 2001 From: lbw <1192299468@qq.com> Date: Fri, 12 Dec 2025 16:12:17 +0800 Subject: [PATCH] =?UTF-8?q?refactor(exam):=20=E9=87=8D=E6=9E=84=E6=91=B8?= =?UTF-8?q?=E5=BA=95=E6=B5=8B=E8=AF=95=E4=B8=BA=E8=80=83=E8=AF=95=E8=AF=8D?= =?UTF-8?q?=E6=B1=87=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除原 Assessment 相关的实体、Mapper、Service 接口及实现和 Controller - 重命名常量类 AssessmentConstant 为 ExamWordsConstant 并调整相关常量值 - 新增 ExamWordsController 替代 AssessmentController,使用新模板生成试卷 - 新增 ExamWordsService 及其实现,实现按年级、等级和学生列表生成考试词汇 - 新增 ListWordIdTypeHandler 处理 List 与数据库间的 JSON 转换 - 调整 PngUtil 中常量引用改为 ExamWordsConstant - 修改配置文件新增模板路径及临时目录设置 - 新增单元测试 ExamTest 验证考试词汇生成及文档导出功能 - 修改生成配置文件,支持 student_exam_words 表的数据操作 - 清理未使用的 Assessment 相关代码,精简项目结构 --- .../service/config/ListWordIdTypeHandler.java | 59 +++++++ ...ntConstant.java => ExamWordsConstant.java} | 4 +- .../controller/AssessmentController.java | 91 ---------- .../controller/ExamWordsController.java | 102 ++++++++++++ .../{AssessmentsDO.java => ExamWordsDO.java} | 18 +- .../domain/dataobject/GradeUnitDO.java | 20 +++ .../domain/dataobject/StudentExamWordsDO.java | 25 +++ .../domain/mapper/AssessmentsDOMapper.java | 24 --- .../domain/mapper/ExamWordsDOMapper.java | 8 + .../domain/mapper/GradeUnitDOMapper.java | 10 ++ .../mapper/StudentExamWordsDOMapper.java | 11 ++ .../domain/mapper/VocabularyBankDOMapper.java | 11 +- .../vo/assessment/AssessmentStudentReqVO.java | 27 --- .../model/vo/exam/GenerateExamWordsReqVO.java | 20 +++ .../service/service/AssessmentService.java | 17 -- .../service/service/ExamWordsService.java | 13 ++ .../service/service/VocabularyService.java | 11 ++ .../assessment/AssessmentServiceImpl.java | 82 --------- .../service/exam/ExamWordsServiceImpl.java | 78 +++++++++ .../vocabulary/VocabularyServiceImpl.java | 22 +++ .../enlish/service/utils/PngUtil.java | 6 +- .../main/resources/config/application-dev.yml | 7 + .../src/main/resources/generatorConfig.xml | 2 +- .../resources/mapper/AssessmentsDOMapper.xml | 156 ------------------ .../resources/mapper/ExamWordsDOMapper.xml | 21 +++ .../resources/mapper/GradeUnitDOMapper.xml | 17 ++ .../mapper/StudentExamWordsDOMapper.xml | 21 +++ .../mapper/VocabularyBankDOMapper.xml | 57 +------ .../resources/templates/assessment_v5.docx | Bin 0 -> 25273 bytes .../yinlihupo/enlish/service/omr/TestOmr.java | 29 ---- .../service/service/assessmentTest.java | 58 ------- .../enlish/service/service/exam/ExamTest.java | 57 +++++++ 32 files changed, 525 insertions(+), 559 deletions(-) create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/config/ListWordIdTypeHandler.java rename enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/{AssessmentConstant.java => ExamWordsConstant.java} (54%) delete mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/AssessmentController.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/ExamWordsController.java rename enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/{AssessmentsDO.java => ExamWordsDO.java} (54%) create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/GradeUnitDO.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/StudentExamWordsDO.java delete mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/AssessmentsDOMapper.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsDOMapper.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/GradeUnitDOMapper.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/StudentExamWordsDOMapper.java delete mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/assessment/AssessmentStudentReqVO.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/exam/GenerateExamWordsReqVO.java delete mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/AssessmentService.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/ExamWordsService.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/VocabularyService.java delete mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/exam/ExamWordsServiceImpl.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/vocabulary/VocabularyServiceImpl.java delete mode 100644 enlish-service/src/main/resources/mapper/AssessmentsDOMapper.xml create mode 100644 enlish-service/src/main/resources/mapper/ExamWordsDOMapper.xml create mode 100644 enlish-service/src/main/resources/mapper/GradeUnitDOMapper.xml create mode 100644 enlish-service/src/main/resources/mapper/StudentExamWordsDOMapper.xml create mode 100644 enlish-service/src/main/resources/templates/assessment_v5.docx delete mode 100644 enlish-service/src/test/java/com/yinlihupo/enlish/service/service/assessmentTest.java create mode 100644 enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamTest.java diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/ListWordIdTypeHandler.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/ListWordIdTypeHandler.java new file mode 100644 index 0000000..2cc2ecf --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/config/ListWordIdTypeHandler.java @@ -0,0 +1,59 @@ +package com.yinlihupo.enlish.service.config; + +import com.yinlihupo.framework.common.util.JsonUtils; +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +public class ListWordIdTypeHandler extends BaseTypeHandler> { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException { + try { + String json = JsonUtils.toJsonString(parameter); + ps.setString(i, json); + } catch (SQLException e) { + throw new SQLException("转换List为JSON字符串失败", e); + } + } + + /** + * 获取结果(从ResultSet中按列名获取):将JSON字符串转换为List + */ + @Override + public List getNullableResult(ResultSet rs, String columnName) throws SQLException { + return parseJson(rs.getString(columnName)); + } + + /** + * 获取结果(从ResultSet中按列索引获取) + */ + @Override + public List getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return parseJson(rs.getString(columnIndex)); + } + + /** + * 获取结果(从CallableStatement中获取) + */ + @Override + public List getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return parseJson(cs.getString(columnIndex)); + } + + private List parseJson(String json) { + if (json == null || json.isEmpty()) { + return null; + } + try { + return JsonUtils.parseList(json, Integer.class); + } catch (Exception e) { + throw new RuntimeException("转换JSON字符串为List失败", e); + } + } +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/AssessmentConstant.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/ExamWordsConstant.java similarity index 54% rename from enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/AssessmentConstant.java rename to enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/ExamWordsConstant.java index a0ef877..368f19c 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/AssessmentConstant.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/constant/ExamWordsConstant.java @@ -1,8 +1,8 @@ package com.yinlihupo.enlish.service.constant; -public interface AssessmentConstant { +public interface ExamWordsConstant { int PGN_COL = 53; // 文件暂存目录 linux - String ASSESSMENT_FELT = "enlish/assessment/"; + String ASSESSMENT_FELT = "enlish/exam_words/"; } diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/AssessmentController.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/AssessmentController.java deleted file mode 100644 index e769b4a..0000000 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/AssessmentController.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.yinlihupo.enlish.service.controller; - - -import com.deepoove.poi.XWPFTemplate; -import com.deepoove.poi.config.Configure; -import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; -import com.yinlihupo.enlish.service.constant.AssessmentConstant; -import com.yinlihupo.enlish.service.enums.AssessmentsType; -import com.yinlihupo.enlish.service.model.bo.CoordinatesXY; -import com.yinlihupo.enlish.service.model.bo.Word; -import com.yinlihupo.enlish.service.model.vo.assessment.AssessmentStudentReqVO; -import com.yinlihupo.enlish.service.service.AssessmentService; -import com.yinlihupo.enlish.service.utils.PngUtil; -import jakarta.annotation.Resource; -import jakarta.servlet.http.HttpServletResponse; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.multipart.MultipartFile; - -import java.io.File; -import java.io.OutputStream; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RequestMapping("/assessment/") -@RestController -@Slf4j -public class AssessmentController { - - @Resource - private AssessmentService assessmentService; - - @PatchMapping("felt") - public void generateAssessmentDocxStudent(@RequestBody AssessmentStudentReqVO assessmentStudentReqVO, HttpServletResponse response) { - Integer studentId = assessmentStudentReqVO.getStudentId(); - HashMap unitIdAndWordCount = assessmentStudentReqVO.getUnitIdAndWordCount(); - - int assessmentDocxId = assessmentService.getAssessmentDocxId(studentId, AssessmentsType.ASSESSMENT_FELT); - List assessmentWords; - if (assessmentDocxId == 0) { - assessmentWords = assessmentService.genAssessmentWords(studentId, unitIdAndWordCount); - assessmentDocxId = assessmentService.getAssessmentDocxId(studentId, AssessmentsType.ASSESSMENT_FELT); - } else { - assessmentWords = assessmentService.getAssessmentWordsById(assessmentDocxId); - } - - LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); - Configure config = Configure.builder() - .bind("words", policy) - .bind("answer", policy) - .build(); - - Map data = new HashMap<>(); - data.put("assessment_id", assessmentDocxId); - data.put("words", assessmentWords); - data.put("answer", assessmentWords); - - // 4. 渲染并输出 - try (XWPFTemplate template = XWPFTemplate.compile("C:\\project\\java\\enlish_edu\\enlish\\enlish-service\\src\\main\\resources\\templates\\assessment_v3.docx", config)) { - template.render(data); - String fileName = URLEncoder.encode( assessmentDocxId + "摸底测试.docx", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); - response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); - response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName); - - OutputStream out = response.getOutputStream(); - template.write(out); - - out.flush(); - out.close(); - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - - @PostMapping("analyze") - public void generateAssessmentDocxAnalyze(@RequestParam("file") MultipartFile file, @RequestParam("id") Integer assessmentId) { - // 把文件暂存在本地 - String filePath = AssessmentConstant.ASSESSMENT_FELT + assessmentId + ".png"; - try { - file.transferTo(new File(filePath)); - } catch (Exception e) { - throw new RuntimeException(e); - } - - List coordinatesXIES = PngUtil.analysisXY(filePath); - } -} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/ExamWordsController.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/ExamWordsController.java new file mode 100644 index 0000000..45effe8 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/ExamWordsController.java @@ -0,0 +1,102 @@ +package com.yinlihupo.enlish.service.controller; + + +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 com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; +import com.yinlihupo.enlish.service.model.bo.Word; +import com.yinlihupo.enlish.service.model.vo.exam.GenerateExamWordsReqVO; +import com.yinlihupo.enlish.service.service.ExamWordsService; +import com.yinlihupo.enlish.service.service.VocabularyService; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@RequestMapping("/exam/words/") +@RestController +@Slf4j +public class ExamWordsController { + + @Resource + private ExamWordsService examWordsService; + @Resource + private VocabularyService vocabularyService; + @Value("${templates.word}") + private String templateWordPath; + + @PostMapping("submit") + public void generateFeltExamWords(@RequestBody GenerateExamWordsReqVO generateExamWordsReqVO, HttpServletResponse response) { + Integer gradeId = generateExamWordsReqVO.getGradeId(); + Integer level = generateExamWordsReqVO.getLevel(); + List studentIds = generateExamWordsReqVO.getStudentIds(); + if (studentIds == null || studentIds.isEmpty() || gradeId == null || level == null) { + throw new RuntimeException("参数错误"); + } + try { + ExamWordsDO examWordsDO = examWordsService.generateExamWords(gradeId, level, studentIds); + List vocabularyBankDOS = vocabularyService.findVocabularyBankDOListById(examWordsDO.getWordIds()); + + List assessmentWords = vocabularyBankDOS.stream().map(vocabularyBankDO -> Word.builder() + .id(vocabularyBankDO.getId()) + .title(vocabularyBankDO.getWord()) + .definition(vocabularyBankDO.getDefinition()) + .build()).toList(); + + // 配置 POI-TL + LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); + Configure config = Configure.builder() + .bind("words", policy) + .bind("answer", policy) + .build(); + + Map data = new HashMap<>(); + data.put("assessment_id", examWordsDO.getId()); + data.put("words", assessmentWords); + data.put("answer", assessmentWords); + + // 2. 设置文件名编码 + String fileName = URLEncoder.encode(examWordsDO.getId() + "摸底测试.docx", StandardCharsets.UTF_8).replaceAll("\\+", "%20"); + + // 3. 设置响应头 + response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); + response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName); + + try (InputStream inputStream = new FileInputStream(this.templateWordPath); + XWPFTemplate template = XWPFTemplate.compile(inputStream, config); + OutputStream out = response.getOutputStream()) { + + template.render(data); + template.write(out); + out.flush(); + } + + } catch (Exception e) { + + if (!response.isCommitted()) { + response.reset(); + response.setContentType("application/json;charset=UTF-8"); + throw new RuntimeException("生成试卷失败: " + e.getMessage()); + } else { + throw new RuntimeException("生成试卷导出中断", e); + } + } + } + + +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/AssessmentsDO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/ExamWordsDO.java similarity index 54% rename from enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/AssessmentsDO.java rename to enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/ExamWordsDO.java index ddfa26b..a417953 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/AssessmentsDO.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/ExamWordsDO.java @@ -5,28 +5,24 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.math.BigDecimal; import java.time.LocalDateTime; -import java.util.Date; +import java.util.List; @AllArgsConstructor @NoArgsConstructor @Data @Builder -public class AssessmentsDO { - +public class ExamWordsDO { private Integer id; - private Integer studentId; - - private LocalDateTime testDate; - - private Integer testType; + private Integer gradeId; private Integer level; - private Integer estimatedVocabSize; + private String title; - private String contentDetailsJson; + private LocalDateTime createdAt; + + private List wordIds; } \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/GradeUnitDO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/GradeUnitDO.java new file mode 100644 index 0000000..8cd07a0 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/GradeUnitDO.java @@ -0,0 +1,20 @@ +package com.yinlihupo.enlish.service.domain.dataobject; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class GradeUnitDO { + private Integer id; + + private Integer gradeId; + + private Integer unitId; + + +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/StudentExamWordsDO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/StudentExamWordsDO.java new file mode 100644 index 0000000..4e0ba27 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/StudentExamWordsDO.java @@ -0,0 +1,25 @@ +package com.yinlihupo.enlish.service.domain.dataobject; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class StudentExamWordsDO { + private Integer id; + + private Integer studentId; + + private Integer examWordsId; + + private Integer isCompleted; + + private Date startData; + +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/AssessmentsDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/AssessmentsDOMapper.java deleted file mode 100644 index 5db630e..0000000 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/AssessmentsDOMapper.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.yinlihupo.enlish.service.domain.mapper; - -import com.yinlihupo.enlish.service.domain.dataobject.AssessmentsDO; -import org.apache.ibatis.annotations.Param; - -public interface AssessmentsDOMapper { - int deleteByPrimaryKey(Integer id); - - int insert(AssessmentsDO record); - - int insertSelective(AssessmentsDO record); - - AssessmentsDO selectByPrimaryKey(Integer id); - - int updateByPrimaryKeySelective(AssessmentsDO record); - - int updateByPrimaryKeyWithBLOBs(AssessmentsDO record); - - int updateByPrimaryKey(AssessmentsDO record); - - Integer selectAssessmentsIdByStudentIdAndTestType(@Param("studentId") Integer studentId, @Param("testType") Integer testType); - - String selectContentDetailsJsonByAssessmentsId(@Param("assessmentsId") Integer assessmentsId); -} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsDOMapper.java new file mode 100644 index 0000000..2250ab3 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/ExamWordsDOMapper.java @@ -0,0 +1,8 @@ +package com.yinlihupo.enlish.service.domain.mapper; + +import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsDO; + +public interface ExamWordsDOMapper { + + int insert(ExamWordsDO record); +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/GradeUnitDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/GradeUnitDOMapper.java new file mode 100644 index 0000000..6ed5767 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/GradeUnitDOMapper.java @@ -0,0 +1,10 @@ +package com.yinlihupo.enlish.service.domain.mapper; + +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface GradeUnitDOMapper { + + List selectUnitIdsByGradeId(@Param("gradeId") Integer gradeId); +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/StudentExamWordsDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/StudentExamWordsDOMapper.java new file mode 100644 index 0000000..ab6f816 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/StudentExamWordsDOMapper.java @@ -0,0 +1,11 @@ +package com.yinlihupo.enlish.service.domain.mapper; + + +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface StudentExamWordsDOMapper { + + int insertStudentsExam(@Param("studentIds") List studentIds, @Param("examWordsId") Integer examWordsId); +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/VocabularyBankDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/VocabularyBankDOMapper.java index 4418c3f..896783c 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/VocabularyBankDOMapper.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/VocabularyBankDOMapper.java @@ -6,17 +6,10 @@ import org.apache.ibatis.annotations.Param; import java.util.List; public interface VocabularyBankDOMapper { - int deleteByPrimaryKey(Integer id); - - int insert(VocabularyBankDO record); int insertSelective(VocabularyBankDO record); - VocabularyBankDO selectByPrimaryKey(Integer id); - - int updateByPrimaryKeySelective(VocabularyBankDO record); - - int updateByPrimaryKey(VocabularyBankDO record); - List selectVocabularyBankDOListByUnitId(@Param("unitId") Integer unitId, @Param("wordCount") Integer wordCount); + + List selectVocabularyBankDOListByIds(@Param("ids") List ids); } \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/assessment/AssessmentStudentReqVO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/assessment/AssessmentStudentReqVO.java deleted file mode 100644 index 00f4460..0000000 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/assessment/AssessmentStudentReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.yinlihupo.enlish.service.model.vo.assessment; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.HashMap; - -// 进行摸底测试 -@AllArgsConstructor -@NoArgsConstructor -@Data -@Builder -public class AssessmentStudentReqVO { - - /** - * 学生 id - */ - private Integer studentId; - - - /** - * 单元 id 和词数 - */ - private HashMap unitIdAndWordCount; -} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/exam/GenerateExamWordsReqVO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/exam/GenerateExamWordsReqVO.java new file mode 100644 index 0000000..da093ba --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/exam/GenerateExamWordsReqVO.java @@ -0,0 +1,20 @@ +package com.yinlihupo.enlish.service.model.vo.exam; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class GenerateExamWordsReqVO { + + private Integer gradeId; + private Integer level; + private List studentIds; +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/AssessmentService.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/AssessmentService.java deleted file mode 100644 index abae1df..0000000 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/AssessmentService.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.yinlihupo.enlish.service.service; - - -import com.yinlihupo.enlish.service.enums.AssessmentsType; -import com.yinlihupo.enlish.service.model.bo.Word; - -import java.util.HashMap; -import java.util.List; - -public interface AssessmentService { - - List genAssessmentWords(Integer studentId, HashMap unitIdAndWordCount); - - Integer getAssessmentDocxId(Integer studentId, AssessmentsType assessmentsType); - - List getAssessmentWordsById(Integer id); -} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/ExamWordsService.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/ExamWordsService.java new file mode 100644 index 0000000..f85c174 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/ExamWordsService.java @@ -0,0 +1,13 @@ +package com.yinlihupo.enlish.service.service; + + +import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsDO; + +import java.util.List; + +public interface ExamWordsService { + + ExamWordsDO generateExamWords(Integer gradeId, Integer level, List studentIds); + + +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/VocabularyService.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/VocabularyService.java new file mode 100644 index 0000000..41f1a23 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/VocabularyService.java @@ -0,0 +1,11 @@ +package com.yinlihupo.enlish.service.service; + + +import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; + +import java.util.List; + +public interface VocabularyService { + + List findVocabularyBankDOListById(List ids); +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java deleted file mode 100644 index 2950db4..0000000 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.yinlihupo.enlish.service.service.assessment; - -import com.yinlihupo.enlish.service.domain.dataobject.AssessmentsDO; -import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; -import com.yinlihupo.enlish.service.domain.mapper.AssessmentsDOMapper; -import com.yinlihupo.enlish.service.domain.mapper.VocabularyBankDOMapper; -import com.yinlihupo.enlish.service.domain.mapper.WordMasteryLogDOMapper; -import com.yinlihupo.enlish.service.enums.AssessmentsType; -import com.yinlihupo.enlish.service.model.bo.Word; -import com.yinlihupo.enlish.service.service.AssessmentService; -import com.yinlihupo.framework.common.util.JsonUtils; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Service -public class AssessmentServiceImpl implements AssessmentService { - - @Resource - private VocabularyBankDOMapper vocabularyBankDOMapper; - @Resource - private WordMasteryLogDOMapper wordMasteryLogDOMapper; - @Resource - private AssessmentsDOMapper assessmentsDOMapper; - - @Override - public List genAssessmentWords(Integer studentId, HashMap unitIdAndWordCount) { - - List words = new ArrayList<>(); - for (Map.Entry entry : unitIdAndWordCount.entrySet()) { - Integer unitId = entry.getKey(); - Integer wordCount = entry.getValue(); - - List vocabularyBankDOS = vocabularyBankDOMapper.selectVocabularyBankDOListByUnitId(unitId, wordCount); - words.addAll( - vocabularyBankDOS.stream() - .map(vocabularyBankDO -> Word.builder() - .id(vocabularyBankDO.getId()) - .title(vocabularyBankDO.getWord()) - .definition(vocabularyBankDO.getDefinition()) - .build()) - .toList() - ); - } - - assessmentsDOMapper.insertSelective( - AssessmentsDO.builder() - .studentId(studentId) - .level(0) - .testType(AssessmentsType.ASSESSMENT_FELT.getId()) - .testDate(LocalDateTime.now()) - .estimatedVocabSize(0) - .contentDetailsJson(JsonUtils.toJsonString(words)) - .build() - ); - - return words; - } - - @Override - public Integer getAssessmentDocxId(Integer studentId, AssessmentsType assessmentsType) { - return assessmentsDOMapper.selectAssessmentsIdByStudentIdAndTestType(studentId, assessmentsType.getId()); - } - - @Override - public List getAssessmentWordsById(Integer id) { - String contentJson = assessmentsDOMapper.selectContentDetailsJsonByAssessmentsId(id); - try { - return JsonUtils.parseList(contentJson, Word.class); - } catch (Exception e) { - throw new RuntimeException(e); - } - - } - - -} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/exam/ExamWordsServiceImpl.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/exam/ExamWordsServiceImpl.java new file mode 100644 index 0000000..6e94ad7 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/exam/ExamWordsServiceImpl.java @@ -0,0 +1,78 @@ +package com.yinlihupo.enlish.service.service.exam; + +import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsDO; +import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; +import com.yinlihupo.enlish.service.domain.mapper.ExamWordsDOMapper; +import com.yinlihupo.enlish.service.domain.mapper.GradeUnitDOMapper; +import com.yinlihupo.enlish.service.domain.mapper.StudentExamWordsDOMapper; +import com.yinlihupo.enlish.service.domain.mapper.VocabularyBankDOMapper; +import com.yinlihupo.enlish.service.service.ExamWordsService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Service +@Slf4j +public class ExamWordsServiceImpl implements ExamWordsService { + + @Resource + private GradeUnitDOMapper gradeUnitDOMapper; + @Resource + private VocabularyBankDOMapper vocabularyBankDOMapper; + @Value("${templates.count}") + private Integer wordCount; + @Resource + private ExamWordsDOMapper examWordsDOMapper; + @Resource + private StudentExamWordsDOMapper studentExamWordsDOMapper; + + @Override + @Transactional(rollbackFor = RuntimeException.class) + public ExamWordsDO generateExamWords(Integer gradeId, Integer level, List studentIds) { + + List unitIds = gradeUnitDOMapper.selectUnitIdsByGradeId(gradeId); + List vocabularyBankDOS = new ArrayList<>(); + int count = wordCount; + for (Integer unitId : unitIds) { + List words = vocabularyBankDOMapper.selectVocabularyBankDOListByUnitId(unitId, 20); + vocabularyBankDOS.addAll(words); + count -= 20; + if (count <= 0) { + break; + } + } + ExamWordsDO examWordsDO = ExamWordsDO.builder() + .gradeId(gradeId) + .level(level) + .title("测试") + .createdAt(LocalDateTime.now()) + .wordIds(vocabularyBankDOS.stream().map(VocabularyBankDO::getId).toList()) + .build(); + + int insert = examWordsDOMapper.insert(examWordsDO); + if (insert <= 0) { + throw new RuntimeException("插入考试失败"); + } + + int insertStudentsExam = studentExamWordsDOMapper.insertStudentsExam(studentIds, examWordsDO.getId()); + if (insertStudentsExam <= 0) { + throw new RuntimeException("插入学生关联考试失败"); + } + + return examWordsDO; + } + + @PostMapping("analyze") + public void generateAssessmentDocxAnalyze(@RequestParam("file") MultipartFile file, @RequestParam("id") Integer id) { + + } +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/vocabulary/VocabularyServiceImpl.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/vocabulary/VocabularyServiceImpl.java new file mode 100644 index 0000000..7e52bb5 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/vocabulary/VocabularyServiceImpl.java @@ -0,0 +1,22 @@ +package com.yinlihupo.enlish.service.service.vocabulary; + +import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; +import com.yinlihupo.enlish.service.domain.mapper.VocabularyBankDOMapper; +import com.yinlihupo.enlish.service.service.VocabularyService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; +@Service +@Slf4j +public class VocabularyServiceImpl implements VocabularyService { + + @Resource + private VocabularyBankDOMapper vocabularyBankDOMapper; + + @Override + public List findVocabularyBankDOListById(List ids) { + return vocabularyBankDOMapper.selectVocabularyBankDOListByIds(ids); + } +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/PngUtil.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/PngUtil.java index 56d844d..42a4b69 100644 --- a/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/PngUtil.java +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/utils/PngUtil.java @@ -1,6 +1,6 @@ package com.yinlihupo.enlish.service.utils; -import com.yinlihupo.enlish.service.constant.AssessmentConstant; +import com.yinlihupo.enlish.service.constant.ExamWordsConstant; import com.yinlihupo.enlish.service.model.bo.CoordinatesXY; import com.yinlihupo.enlish.service.model.bo.Word; import lombok.extern.slf4j.Slf4j; @@ -91,7 +91,7 @@ public class PngUtil { // 获取每一列的宽度 list.sort(Comparator.comparingInt(CoordinatesXY::getHeight)); - int height = list.get(list.size() - 1).getHeight() / AssessmentConstant.PGN_COL; + int height = list.get(list.size() - 1).getHeight() / ExamWordsConstant.PGN_COL; // 删除两列答题卡区块 list.sort(Comparator.comparingInt(CoordinatesXY::getWidth)); @@ -139,7 +139,7 @@ public class PngUtil { int currentX = coordinatesXY.getX(); int currentY = coordinatesXY.getY(); - int count = i == 0 ? AssessmentConstant.PGN_COL - 1 : AssessmentConstant.PGN_COL; + int count = i == 0 ? ExamWordsConstant.PGN_COL - 1 : ExamWordsConstant.PGN_COL; // 内层循环:遍历这一列的每一行 for (int j = 0; j < count; j++) { diff --git a/enlish-service/src/main/resources/config/application-dev.yml b/enlish-service/src/main/resources/config/application-dev.yml index dfbd476..b8b351a 100644 --- a/enlish-service/src/main/resources/config/application-dev.yml +++ b/enlish-service/src/main/resources/config/application-dev.yml @@ -19,3 +19,10 @@ spring: max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) min-idle: 0 # 连接池中的最小空闲连接 max-idle: 10 # 连接池中的最大空闲连接 + + +templates: + word: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\templates\assessment_v5.docx + count: 100 +tmp: + png: C:\project\java\enlish_edu\enlish\enlish-service\src\main\resources\tmp\png \ No newline at end of file diff --git a/enlish-service/src/main/resources/generatorConfig.xml b/enlish-service/src/main/resources/generatorConfig.xml index cec6772..8a856d9 100644 --- a/enlish-service/src/main/resources/generatorConfig.xml +++ b/enlish-service/src/main/resources/generatorConfig.xml @@ -45,7 +45,7 @@ targetProject="src/main/java"/> - - - - - - - - - - - - - - - - - id, student_id, test_date, test_type, level, estimated_vocab_size - - - - content_details_json - - - - - - delete from assessments - where id = #{id,jdbcType=INTEGER} - - - - insert into assessments (id, student_id, test_date, - test_type, level, estimated_vocab_size, - content_details_json) - values (#{id,jdbcType=INTEGER}, #{studentId,jdbcType=INTEGER}, #{testDate,jdbcType=TIMESTAMP}, - #{testType,jdbcType=INTEGER}, #{level,jdbcType=INTEGER}, #{estimatedVocabSize,jdbcType=INTEGER}, - #{contentDetailsJson,jdbcType=LONGVARCHAR}) - - - - insert into assessments - - - id, - - - student_id, - - - test_date, - - - test_type, - - - level, - - - estimated_vocab_size, - - - content_details_json, - - - - - #{id,jdbcType=INTEGER}, - - - #{studentId,jdbcType=INTEGER}, - - - #{testDate,jdbcType=TIMESTAMP}, - - - #{testType,jdbcType=INTEGER}, - - - #{level,jdbcType=INTEGER}, - - - #{estimatedVocabSize,jdbcType=INTEGER}, - - - #{contentDetailsJson,jdbcType=LONGVARCHAR}, - - - - - - update assessments - - - student_id = #{studentId,jdbcType=INTEGER}, - - - test_date = #{testDate,jdbcType=TIMESTAMP}, - - - test_type = #{testType,jdbcType=INTEGER}, - - - level = #{level,jdbcType=INTEGER}, - - - estimated_vocab_size = #{estimatedVocabSize,jdbcType=INTEGER}, - - - content_details_json = #{contentDetailsJson,jdbcType=LONGVARCHAR}, - - - where id = #{id,jdbcType=INTEGER} - - - - update assessments - set student_id = #{studentId,jdbcType=INTEGER}, - test_date = #{testDate,jdbcType=TIMESTAMP}, - test_type = #{testType,jdbcType=INTEGER}, - level = #{level,jdbcType=INTEGER}, - estimated_vocab_size = #{estimatedVocabSize,jdbcType=INTEGER}, - content_details_json = #{contentDetailsJson,jdbcType=LONGVARCHAR} - where id = #{id,jdbcType=INTEGER} - - - - update assessments - set student_id = #{studentId,jdbcType=INTEGER}, - test_date = #{testDate,jdbcType=TIMESTAMP}, - test_type = #{testType,jdbcType=INTEGER}, - level = #{level,jdbcType=INTEGER}, - estimated_vocab_size = #{estimatedVocabSize,jdbcType=INTEGER} - where id = #{id,jdbcType=INTEGER} - - - - - - - \ No newline at end of file diff --git a/enlish-service/src/main/resources/mapper/ExamWordsDOMapper.xml b/enlish-service/src/main/resources/mapper/ExamWordsDOMapper.xml new file mode 100644 index 0000000..a20075e --- /dev/null +++ b/enlish-service/src/main/resources/mapper/ExamWordsDOMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + insert into exam_words (grade_id, level, title, word_ids, created_at) + VALUES (#{gradeId}, #{level}, #{title}, #{wordIds, typeHandler=com.yinlihupo.enlish.service.config.ListWordIdTypeHandler}, #{createdAt}) + + + + \ No newline at end of file diff --git a/enlish-service/src/main/resources/mapper/GradeUnitDOMapper.xml b/enlish-service/src/main/resources/mapper/GradeUnitDOMapper.xml new file mode 100644 index 0000000..3f87644 --- /dev/null +++ b/enlish-service/src/main/resources/mapper/GradeUnitDOMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/enlish-service/src/main/resources/mapper/StudentExamWordsDOMapper.xml b/enlish-service/src/main/resources/mapper/StudentExamWordsDOMapper.xml new file mode 100644 index 0000000..c4e802a --- /dev/null +++ b/enlish-service/src/main/resources/mapper/StudentExamWordsDOMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + insert into student_exam_words + (student_id, exam_words_id, is_completed, start_data) + values + + (#{studentId}, #{examWordsId}, 0, now()) + + + + + \ No newline at end of file diff --git a/enlish-service/src/main/resources/mapper/VocabularyBankDOMapper.xml b/enlish-service/src/main/resources/mapper/VocabularyBankDOMapper.xml index 4616f99..0588f87 100644 --- a/enlish-service/src/main/resources/mapper/VocabularyBankDOMapper.xml +++ b/enlish-service/src/main/resources/mapper/VocabularyBankDOMapper.xml @@ -9,27 +9,6 @@ - - id, word, `definition`, pronunciation, unit_id - - - - - - delete from vocabulary_bank - where id = #{id,jdbcType=INTEGER} - - - - insert into vocabulary_bank (id, word, `definition`, pronunciation, unit_id) - values (#{id,jdbcType=INTEGER}, #{word,jdbcType=VARCHAR}, #{definition,jdbcType=VARCHAR}, - #{pronunciation,jdbcType=VARCHAR}, #{unitId,jdbcType=INTEGER}) - insert into vocabulary_bank @@ -69,39 +48,19 @@ - - update vocabulary_bank - - - word = #{word,jdbcType=VARCHAR}, - - - `definition` = #{definition,jdbcType=VARCHAR}, - - - pronunciation = #{pronunciation,jdbcType=VARCHAR}, - - - unit_id = #{unitId,jdbcType=INTEGER}, - - - where id = #{id,jdbcType=INTEGER} - - - - update vocabulary_bank - set word = #{word,jdbcType=VARCHAR}, - `definition` = #{definition,jdbcType=VARCHAR}, - pronunciation = #{pronunciation,jdbcType=VARCHAR}, - unit_id = #{unitId,jdbcType=INTEGER} - where id = #{id,jdbcType=INTEGER} - - + \ No newline at end of file diff --git a/enlish-service/src/main/resources/templates/assessment_v5.docx b/enlish-service/src/main/resources/templates/assessment_v5.docx new file mode 100644 index 0000000000000000000000000000000000000000..ce38579d2235291a362196122aba9fef119bfc3c GIT binary patch literal 25273 zcmb5UV~}V;vnATL&C|AR+qP}nwr$(CPTRI^+ud`%J2Ua-PQ3TyRm4W*UKLfDYgbif zR>?~NgFpfNr-aEK@c-xezYFBQ7h^jkc}F{YCp!6mFqD4+i2s6>hAQ|N0RjNH0s#OZ z{GTubdwW`U8|xg!N!dYqgl?jHc!iq?+%gJyn;1!dz&P-6!EF%C7S)gX@P z`8UDHj6)*#fztuf^xTxVbm;tWLd;Hvr@`00V~~L(AoFaNfA60l|7u45xlB*D6S+kk zi@@04{f@Gk+D`vf`<0Siuu(o+Q5|ED$He0}&L*#KZdFqZR_ENUUKxLsW3U6AJcJ)x zk4|lB3p?mFMsIT&IZ< zXZ!DjI84qkoBSgr?H?hC|0f|vc8(_hVI(F|PHK=IW8^k)x8P`p^!$rSax-8;77m0g zvMs)QGtx#gvE8<9f4oX>Fo;cnFRpXeV8f-QFc1NV?)LF(hsao2JkZ&XAcGjd5T^eaJ!JC5RAe-irQ916BuMPUJz`- zT$(4cuNh3a)Q@E;CzerasSKpvosv=0eG->n0uAw7pnc>gv!_b zQ)jQAIv-zeU-i{bYiCncV{_z%hhpjUSnm-rphNT5kDMzl ze(hledbvtHv_%BB$-ld?0Q))*5B(m#{JB z9|+tO%d*r}WpdI--{PY&vXbi&+UJd4&Bhvz6XC_+{a7Xj5Ovg^XDKMApU~ht?ny-- zTE+zD>$P%81y1vGE znJBvsa>%e%8M`X-(o&ARy7xXQ4=*DNt2J{O5f52ra@p9Z5_qU9T^jjns*>%5r}#vu zgdI4q{@lE$(&@DZb7mRa+36bU?u6~|MwXY^dF0Ec8AskD;aXNG-nfbjW!zs^ZwyUlWs z1xc0s{M=?=-I2!RiRF}j@AoZF#das5Q-i4!A@e@!xL^G~o=*9Ev~x~|t%jW70TREM z;YxoR$rl#KiQ!YZP2X#!J%IFRjnSHbcunP!(oOUxoyp=ICZHJ56UcSRz`eXieL;!J zd^1SOP={ox1Eo(K70Q)lj|~ZcbiXTR$6EJU%yh@{qdgSOMIF#O6^lJ$(2VBu zBPOwq)>pz$*(?3pHbSjQn&_vE|32avB8iQ>rAHJ8tn+OESmLxn$7ZFU$f2NQ_x6?j zTCF$qnKiXLc5!x z*^1cw+5_qJTBw%KV#b;N26a69^o011X_v)J{A6*!^=BeOS$$iYGNJnmI zul}*k{?0mzTdE9fL>p%bc{=WMCzQujG#M=c*^ZI0) z|9sd@#x^%hd|6JLi_3~HXH+oor?XJb_K4EO6IX|MbDQ`W3oBJ>q~=hPJh`?LYPRiQXbxq^fOBBSIWT7* zf*&M^4HL?S343#39}1HV7l@!qjPH9Q-h%kMZNUy?@8%c_u$9wD1mH&Y@K1Ek^G~!8 z2e73BMFfD1B2EAt`5<2pGF+e!DJj&Cgc2G^LJkcoC65A?noo>EEg(Ry9_UDqOK=OV z&6;TVH90RhL6`vg3iTrhFpC7RI!I0kwETU58KXyTKYiSH!R(sZvl4N0;{qoA+Q9_~ zHtty}wz|yG z6IAZlONrgMw{EY(Td!Z~y*KdoBd*F9f!dG0&@)>2Y^HVm@Y+BoiouFYI8BMBtSl)h<(Og8e`;9WSDCtgG%j~v7DZ{aOxzxPNVkGA=g!*Cc)u$qqFzZCxogWg30_b95qb@ zw^y0N+;d}Q`R`<%_s{CRkJGok*P%~nNA?j*sbE=V+;VFkbfi>CKq^7Min`FCK0h%% z3mK$o0!#nwmkTXsbvBkyuFl}@;8VFSxUxNEBxaOSWI$2tFW`_Ls8bQj;$r3=dMc#L zWYOX;hKwzp-oqcy0&oeL%N>5*8NWlo{yXI?IM0eeDF?nETwZ~`>wmYj(Y-t|m=&Ew zYIS!x{*!`E&?8zx3}7|d#Dpm{6XCRyQx;$y6icv9`myrpr0gY$O#cP3O#dyBPmQ`NlF>diHloQeT_y;`%E<4ISCUF*QJx-U^~Y6zPaTsw(NwOfbA z{qi!vX~rRN%jFDIEoYASoe=aJ4ZH~EYHk0@%KiKc$`};a&;BRMhhy{={-43x!yN== z-Gw*xPr2T>I&d`gPXnu$*Q?ckg6EOBT$GxxqWc6wH^WUlPe86=9&eAFHOsb)8%$z& zvQ{Wtaxmt8)gjm)fQ0&v-yH7&p9%ohviy<(G-#pXNo)Kl8k|rOvP*t6E%}ucm-1b0 zVpsCj6-};WbE|n=$>x?1IFb#`&@d#Mno;10R^*ccVpbLNqZ-At0&P|s^L9~$0^}@i zW?`eV_$GHFF2GiLn@G0QVYGai`;o@A!bP$jb=qimqmJxhY^fva9d$yp`w=9LD7Mrw zjqU_t+k+UAh6=$VC#!>)<3I9iTScR?bL=mld?d5e|V~8uMOc+8M)_@x* zcGglO~IyqDq_pAdTZfLQ80N z#0g#QM7Il^|3ACeAI_FKSqd9d+WJoesJL)JJ{;Y!-lLnD8 zxqm&vxF2!+U&=KI{~z5Wir5;$mI_3)qfV2-#*(r5kN%|aunJoLY;bcBqiu9XzN3z& z<xm+DM6jiT5dE`j7Ate+#{Xk5D?;~jpU?NF3xb;u z8-)5l75=z6N_;hVwMD7q{figNO>b4BvzK_j^YGnO061&9IbgS-@@8hYJ0SV!#IoP& z#Ef04bO7=+b9O*yU!}gt{GXGP7uNt6Q}}dCP@_Og*kD zgKmKSNgE^SaT8-S-K!|O`=Na+s~&$#cuyi+F=XJ-prv;XOQs!7>x zvLp1;)&KU->8>{HG=_AB*l5T^7SmK3dzF%GoP-jBCN^Gtec&o4Ue_+?pBXwqvc%c3 z!_P$Tc7Lia%mYG8uxDw_#Ms2pPkIQf+1m2(lpa>FBtkU+Da_n$aT;3mczu4_*=4F5 zmhel*0tAk?Po1vlR#4_j8~2WqB(il6z@%%@N*3=aU6GO?&APD6cxVuaum&T!WVg5C zYU0A=X|?D(fHTla(6%3Gm8cl@ZYScn{d&UQGr4L_2cNn`WH6$U0V%Zun>or+t{0{B zMS{`Zc*PG|$>K&j`l_s)rT+%uu zu#9E_Tvi*$OmO#ZGSJS23VN`{+VzBNc<*=PusDQ>z@3K<#9CVwVzzJX5pqd}OeQmt zz&ZfS4j_xaiPbi0Lg+Rfpb_N*jsON!<-Ek}Pjqx5QFjV{Qu}9I5&{!X?U5X*qwhgB z{MAxcdJbpJI8d>hapMXK;fF20>%kw^QzuU(%2Xi-SERcDF}TzaFcz7=PlbK}$|cCm zc3Hg}i0cuB-=27_=Sy_edh(6~ndqON`GS&$koA)O&IL{0Jk30=WUZ@QWP;Zb(;et> zm>6F4!FCcxoOA%ImG9=J%)Y}?ESJ-hD0mSqpTq1JJ@Zc^_e(_?{}y~D7_2@o;vX6+ zBEdL+ZiDJnX(iKo2Bk755k*7w6Drt74z*)gzh1@-3SWe7sRL4pd&~NeT-srGX#FL= z$C7A?2Wcqzd-g6RW_N>js9|AHf8T^ z8lQK7X8#gF@+(y+dod;@DE2O+OEH37DdR&Rdi5IsApRWh=F!&&tZo;8DQ9I~{x#np zs%%3LNdJKQUks*mS&E835ZoFLqfqYZzsqaI5u48Ei6Is0V?TGGJ05vpZk^g-fZIF>UILoS2PG;%EVv)ZoskOq*G=wrBNvBR~Jt5^KZ+2sqc;4T`YpJ(nhp2)t}#7s8H zRm-;^0!86HsRB|JMzHU@iK|cT=%)z1$Hy;ZW^v-QLa7l;D4xD^4Fu&!JeP>wt@O`< zk-C^{$q2wI=&yLQ>!0*T4tQWTD_d6hp>6RT>}Kx2;o|7ccTD8co=GoTFs#wgsnfUe z-~@qXXZ9@&L7^)wLSN6gIT^6|OgcICYkOvE(!SlF6|97?^9dKdsD1fWmf^OhXEL;u zXW_-FDBdXb^H}h*ayIX^2ZQLQnefP0X9Atw=76pdi#rE1PM(=>rGsp0QXXfU^UFZZ zG}jjO^K1`zm<W9%4b;zCFiJMP>ADT3pOo|%%#YuIgaZKeiJhJCg)C47y2kc{6f#m^$oa&G@Xn60s+jR zCy}jd>HaqVGK1QOYn>~^nP%}No%mt$Yy-IxU`!SX!6^CI?gyF+ag&NfRNj^s4Or)u zK1*{pt71k%yrkaxEG6HB&2IVu{C_74p#OI!z}eiy#^nD>8Em&;JwSm00CZsh0HFSN z;D5mx{*ySk)|ie%W<%*yyZEKg|1hIA_s19gZtcn(Kd?< zJkW+>p)c&NV?H3!_f}TO83k4Jr90*gzIM-`$@UZ~4K^0W4 zO=vlCeXfhA0y!wE%uOO2bX?G^(Y3lt{$TN+P|>biw|c@V;f3WdLTtCNc5t?3waRe~ zXyHGZFY0R3sJY>Em--Ds&2p={&MCjxV;MWEtJ%wXc5OR3U>V!Hq`NlH7 zi$V3S)4g2sg3}FBlzb>cbw*La?(qzuB`^|w?Y=XuYwTL2*@H%@FDOpUG@NogC(#VO zVd#a{LyaVqc;KHQb&nS1--IhP9+Rk-OH{7pm_rj?absdeq7t&~pu@ND}(pt@Ois?>Yo)&AZiJF7)teg|3>%Y`(b{&Ja?nR^En=c#{T`3 z%lm%0O!LK8o*T$U*W-IXTu(yZ_5L}9kK?|>jSXe>=4Yf1ynG{WA7`|e78`ECfq%c0 zfKe2-j>zFgBp0(pi~Gry#(jFmEF256GGYsC<8Vg6yPJ5*HL~YBR+8Yxsw>F==GKpH z#e5ar&&-^JdeqM(`x$><8rc>;TVrzrKbH0NT7-O(IZ#@cXfQe<5a|$xoN0hrVsi~{ z813o`W_RGhCz=7lp4_8_+z59UV#F5;lYuVaNfgjEVm1-B9P(RI6ThzhRc zUo!dvP$G*~n^P{FDs%K_v;_CY8(%Ur_8SpnttBHi=Il=f?ZJTFPwMFJ*P}XL%@256 z^yN3ph&oEdd3U}c8nHG`!*x+imhoQW9aqBsj9>?fEZmEi!29y=KrP%MfG5|>y4eKb zI_K@e-U{5Y$ZEz9|6n}M)zY8M;7vF5vJppV{}?&0bx8+_W2yqJ@Fg zv;mve)w=Cw`W^9-CcmD<9R3u%?E$6czNdwuB=DISI;OBr94MkRn!DLUij~X|M#)4% zluq|=GH6##`QR^GkGWsVrlFelM42X=J5@-RmrFY{y3jTm#$j8%U?HW%tZ!z9cmV*s z!qGHnu{hc{_a~1{i3kb44*UMfjB;?!$Ndo6#*~ydgIaa)Ui{rXDVc~F zT#cGbrD>)xEW{p2^Jz`ID{VFHQK z9_wu}qv2uysEA;dUS2X+TF`Wp(qg(+@9M*?CGG*7rn^AiS-xP+S;+t3KWy4wxLQ+q zGSSguGz0TE)0n&w8LQEOGlRa!v}UWp0?$zW)2b4h#E*?3rw|Q|rwq)-`xmM2jzDpH z;xedf1iMD_+?z~a%!5KdY8+=VdH9iEvy?_uXYm9M2z~3WvoxpWTRl?PsMeLj_P3V? z0DD3?g)6Pmt!B5we~Zz|z?^&c$D;M@7v<{HkF^_Irxg0NMu{@y9005N8~(pL{oKGJ zqNHp90O~P-|FzlkUrztOojMmA6IC z-B@6Pipe99*c|3p&h+TbnWF>pz|-42GJKi!2k-JSpg+x~AQcr$DjO@AE_Zr0uBwGq zO2t8g5XoePv)>M~>3(kd@VH-X%|sfI-)?Us@aDdAjhkGnJbVY1~keUprotf41OAK@1Ea(b8y`*ZUxRg?CVV8KRglt5QJYf zmWgFCdxLj`c^+WIUJ{FqVK<~%2fn^q9PwpCKiz2Je~k(0=0E05BF~exxF7VqCrxMo ztz%Cn91CIs@#%8xhgo{t*vbztmxy=KsTiJiq&a`a=ks~&woJ@g0Fx}d&Y|Ejei3(( z1viZ0J#1;iwfpcjUyO3_k_~R+(7tv1A9{SqY3rd__7n}7QP;uOX;_8mv> zirIe<{u^qak3(ZDF<=|4Bz(%Jxkq-t{bO+aHvah;(z2w))x|BbPv>Tw&DuA5`l=rwUHMW;4x>`ghyh9$qc=51HYW zKZ0zPnxVO zJ6`dGi2ks1V6r&5+gpRg0Qdr1!NqL6K{{{Kh3@iM>FnhP4GV#KHWM~r^tSzubhCnY zim~u9E2u}ke|esGQjSo0IO78CT1hEy9GuY5b(+uO<@Z6c+upH1_E^Un=sT@b8+Th&SaH=uKa zPt1_CKA}I3c^>CYZ8>Kpa=TBJX}ni&4Ndix==0v&hlh1uI8`u}oTF|%RE?K>v6cTV0Po9(@_Nb)472fP#IxbUMUtrE{ncZ88DkP4IK zYT~Q0E5e+>J*h9?llrALzwWQ@7R>4#XFG4C@_L&m)OfyXEIw4c_*eUQRCq6^*7qa} zD?@0KrTEC+s~2lNC|J*)iX+HUFa0&ArIW3}ye;R3dyW(9GAv3xZa7aoKxRKCTwvN= zFScWb!34XWLLML6UWlFmWlSla)_kLVcyPel549~FTitby)sduim*1ZH>$|r;Ltv%i zmxrH*6FocA^RkxdOg>O2AZ0!dZ+*km%AyOz zXP_GY-dDc#Z2OhC{+`4CKIQp+46ZV>(H zZ>_n%chtn7h!zn`d}G#pug^bpCw3zqzVGOnD~}d=wb~9A8M4xt4jzcT;1ncui=Q5@ zyLsI}DSEe^=4x;1vv@YkPSDj^`A2O(H}H^oujG1(K*tigXC#Opn>oMC@(1-5ngoBk z+wO?&c7wiZncctYG1MdX1+R-DHBLxO%wn$hv9X5szWk6}u_giw z>3|l)y+%60d+a}4H8H){_3;k&d~qiEsULec*M5^w zwqnT68Ef`dyicMH>1`l-Qh@kju0E!Qo%B&qkvL>aR&YSLVgAl8{=Gg~K23gGrJ|CO zs4k3OGK%?7J)V#t6lpk^roNv}O)&)#5D1x5lf@^7yl2ZFZ=+NTBtzBJA0j-XN^}>V zFGu*8u!Nzwe~9V$uDJk^u_(^Z{s!;8zfV~dk3m2wh4`2l3kP^=MldN6LA2+SpbgYc zfOt%egkmtAN0}arbPg)_tvUpts>-huBsiKF>n=qoM{pG3mWiwsMk}yOAQpJ!Rtax( zL?NLNrw!)e_itU8x(2&wR>LX|;N zXu(8>7J&+n1h0l0xC4~IwArEa>xe}F^GQ$)cM6FE6;(c)Kw{lx<#r8`z;&^UZTY%2-tJ!KxD;<64aIquW*rZfF=E*_HP4W(G`xM4Z7&5 z2?(xNcE6t)2qAPuKf;_N$=>i|4bC_v8pC4WZi94tOk>g6&AfcD=n;>SF?H{VVH=b13KN8o%DegdxKjaEbA zF~J)nN?|_!?I7{fI0-0QPRqxL0QTRT&7$4S|2UxZxd8kP@Wyu==_BW|4+*CYKK=AG z9M9iFsQNc>`(->(0rci|b~j4l?7;wHAnrc)6wajGf$;{xx7pMLoZIXVjMG6w59Jn~ zm5>MOAu|OLuKfGX{Mw^&>6~k90p17FI6UTNCW3SWBtWo@>Vy5?NCtss5(op4VbuaUpOVUHpAa(8$>zeCBZrqYfhai5U&QBY-gkXF^k{ zt6L?M1_FXg_vEd$Of7BF9#MTDqM{EcLV$6LyVa$AjHZn6+Ee@-Sb0P)F z{BRsOq&B+r-#nQKj*M^qOaIr%P|yV;*KlSZbw8*@8;CA4vkau69tHRSlriZGxSkRr z9C=Rym{$QMh08V!-iurvn_bdzoFD#D)Gb{N*d5*RC+7D}@AtIK?{nAfNyF9DORe9< zL7E20H`W#e{sO@-Z=2Ie-3Py}1HtgyV&7~e8pqy%i z82t{glHT7oahV;=6=uumDQuAO;Z84~|3lp93EbSDPQ34Hf1f@wDH7G)?QQV$6Cx7? zOUG}{ZJIp*U2s7P$(svK+pK_tcFy)MC@FlCFx z=PLVW@H&sZZ|!bd>fDX`U?Y&9GRF3h^q+P!fsG|A(5UWsoS^)uQ`9crL=~n)jwillrwVZdgxr*d{?7I3UKNP{<>v$b^@oaXS+Jd$Xk+uNYWvQI7y z0y(+u(Q|=@eJy&!{LAjXxVDNvfZ&l@j|TcE5Sb%AY+d=7u|q`1hj!7UyMIwAz~99o zN+x*^o+7;7+k@wAfB+8(eW|=t;->#pnuQrYvzsPaz5z|!C$(na zhq6gQps$0RXV5j^fn~Vjz}lhBI_-OB6m!VnzTOAD6?mtH1@{0*2Mf+~^fF!on_( zp0l>QKt`h7I`Z+j%^K`$cHJHBXua;XO;&)xXu10GPJido=Wymcrm|5^?d)3+J3nbe zkd}5&_4Xkd`ct9r51C;TAjDYh#(+5_opM?EHlCcf)xUlwaZt7x*&842Hq?=wJ1}RySZ$~VZBn3)C%Wg;EUgAn5`B#o2BH;X6Srz%Hk;cCCQvo9 z<#bG$^|CZVNy>=kHYJG~hXQckeU`B1qc_Wgvs(M&;4ASoWkehioxR*z|!$jxGi1N222 zNKIpiIO^JZ1%w}_UM{K3B|;KpWT{>gs_v05>41`5i2|uy|~J}$<6HwXu9n%@wM#WSE8DtNi5Z;B4M$9@r?@P0GA4K&x#?WHtG_M2 zcb-d23Z@qDp5cnb=+IP$Ou zhS|#TTCXI})VBcd9){`C4puZtq_4DMPK#CKZw4@)*{&7~ZcJ$M#mX=6WIO)2et)YIqhLY3QaEAx#PG;vBj%Hy4_*_ggMIlz^h5#X#f|ksb!FE>$f`1 zXzc;kuU1vKYKjHckZ}gc7I~04$bu2(QnB5<5x$O}L%yoL zUXG&6YI&=x+|zZaYR3KCs&K%)O8d9^aM&8;wmDUqp?HrlM<;tsh6t z*=JDGAI=h%%Bum(>M5MM61ll6aay9p-KGg|4t(H99fvSmj>-D&q*G01M=)!?+?F)q z{BWE;)hfdZ=8_QArunsaw6v!W*Qf=QvX-5+j+F!wd&tNj;K1XrO&tXE#8|N-8P!h-W9b8r~B5ga&gbh3|k}gad@G?t}6$~)l{H`O; zU|-&-g-WgoFUbQLPHpQXXFih;g~{j=^WEgY%A1eHl5O--?F?v8+ciFiOJ>Y8)h7x~ z@sH!xqhlQ1}$Ma?>H0oo>)4=%k+M&};^ zlZ-ggZwAGx@Qp>OFVQjD^HHnYOARfy7X4igW*bjq#=ShtZU^?QsAuPU@uZ$&=+d3t zdZKT+Z;MoE0H_Cwx5OAbdx? zuM&gDjNPp}1#D|ojK*7Ic~drNUyotCimBW0SZ>^R9f$%{_G;6N+^?&0QX0_zPPsnc z6V-;79b))R`OTCRd&xV>u+NZ%DLszi{QE(NOEwC!Fm)x}7RgR%>xDN>phvi-CSX^t zkkU&7h_SVr&g_~(PHqU=TyI}8MBqey(=~c99gap>8&3hck2&wa)wOSLR{{?6&_UMO z+|S#*?78U&8cbkV9ns2vfByI(a_f(s+lHb4B3$$VSPpUVz!_j78FXI0zD`;nr`Jh0 z9!vKs_giJaLt}V9Q;dZovUj(S`{e|jQf1i+$a4}Bd3>wNceP3AOoO&-q* zonwUl5`Mi>NRB41SzTVrbE>!SV3!o}3Z6TLt{@0$r?f>a@KE^tC80&w1r7X+UGNb* zf7w;&EG%%o75@Q;I~DMkbq_8alVP zO3Z98ac8Zc@)_3vaBXbFUN$FVh%&9nq(0$N+9Laom2E{EeXeZ09kQkX4SlnKYkLE- z{4ij)x*imwzkyY)2W($=@tQDZ$#uT=bcv8W9yh>I?XuuJ9m!o6KRXw+W!-l8>q^;! zDB?y5CGs?jkq$yNSG(L&NhX_zbwMEGGkKbM?SO;%XZ~Jl|G&5~posRHUaA=Gqnpmh1A5ov~p z|9m2Ap`!c5EO(uBzEwoDqIU-xRd4%kQq(P>^rP{zF_5jshyUE0|2z=ay&u4xO>dP? z;5?j3lC7iA*)Rq7h#Ys(qCQTclat~3Fc+`dbd>klURwqz*_PfTc%cP*`WdwZn>`ba z&eFibGB`w{)sM!u6D$0_K$GY2?HlUW_Sbq+hf zWo@T+(p7miC{sVA=Vn4ZIy6RO&S_%#FZP9U(`sIsi^&q!AFE~DzUWF>54KA8KgiV6 zOJh>uftDTJOe-LII}VX31fIg+Q*-oIxFUz&X!_o;MiQTmZs}0dz+ug{m9?E?PgY`f zGcOAAY3GT32WhN1EECalUCTYohAvPYJI2%2E6exp`HqVYRu&-x`ciRjQI?yy7rJE~ zjiulAO#5ofkRnti^&#elDkNa3co3gBUi>r;S{l!>Bm9{7xD5XDo`b#Wc**g6$2AqE zo!6yG1Zjh*s#=+jQ88iD>$Q2sBlTOWn=QZ|(pkk4u|+t_M)|+typ-T&k~INiJPz`q z6a3bzX@|8lLnOtmCqk}cvz5ycEnE!>>&D)StATg?&0Ib$OOaGXmfJ-ux;tq>X zl_;ewO`PbQqpB+Gs)39>tl}uHW=yp81q?llrB@0!^n0XIIh)dob0Iy~A}Ea=Gg}+7 zt0bJ@!InN%9qVVIlu}X3;n%5sx!I|`s;@k+?g;PKol2Kt61RIbX2fpcQYdO|g+@bf z-RUliVqAYTioC_ndyAa^4cc4s?aO@Uxap=UlhA93zg&HPN|JEvrds>{^Of^2bpsa= zQ9Skc_1i)M(DXW8aHmwpqhfGghiWrZd9BQbX!)|hXXLl>Bg3kR-EVc%S} z?J{?rJ#b`WJgggXYt}%aqK-)K;F3cMqEG<{>xKAu%An4IMtQ1Ct<18h|-xKJy*CNAVxif%S$dc@h|id0eW zGUmFRO7n!T;JUL&KP;?8tc`?9dKBXLvftJn*>csu2`X-5_f!dFUXn)AE3VserT<*= zY&z-8PE=ZZvGQPee&7G4OM?`7TVo=2GGqQu<+$#jX;&Qh0|2#?{A-T=*~#`N=2M0! z;?)vcMgwxWeYv(5ZoHOr>&k3XH=mLR(&jRut!DV(1|rs|`1a5DPP5v@VpzbO-o1g3 zCN6W4Xncyi6j;rB?JWa?WsZAjW^f8ftnz}GV9PGH$Fu4uv%sd0^2WQ+NO)L~j1RJ> zXwiY{XO0jHIgMPzs0$aF6Z<5iw6;;Vo{O-YLSY3b3#ARCZTR!HrD64pWP7GcEQ160 zSYQh0fNNcGZ9&OH!{rc;)Q4}=uGXT3r!}22iQ8V}3o0{?b9zK84M9_dYLdbueL33^ zO@MV02MKm_Yi8Jzld#zmp4EPRWAZTPVP~mxZ2@<)f}2koZ%$Wb9ByYWf4KxsyxCLDx{Hv8e)6P(@=eTB=V+6riMMzKrY32j`&=~rM#a<&Xr+h^ggoxXCxN}Hu$H|5 zCEKei7Y-{q&n<=N=8J_nKn3GvvLxKY}0hhA=`^eSX}37*Zs4h0B0mhbj?d5yaGRR@uc+Hnmd zrkjZ%RF+rIX3VKc0cUBMi^sqe_OkJyo#i$!`=sEYvC59HR+(<>py-@!1vk8`YppD8 z(2TPNLDj$lEsLoA$YA;S?8%gb?jLxsqDs8j>opCwWR>Zd)I}12JVA?vnH*lzm~qot z+*RiwHVm($Mgg^p<#Ab`E{Zm=^LU*~CNc^I1vfA_-o4lrO|^JxCz*9}I}%x*b}oU; zl9GVZntuyX;JRl61zCWrVp^*`hTQRB@j_8%%Y7yrQ4_M`LHI>;SyGXav}fuywK(M0 z8?*iF_HEmbZ9~?`fMYsLS;uTL_U$ z`Fg8Vz%cxziS6yPOJS=7o2W8bvK6?+?siAXjXW=>!KPeC0;keO*q@%-`|X8_ z6w-O?9FsE{O&`PhOsX29{Z3U!`f&~A>p0D6lP*2Jp^1W(-{Ee4~AtgcHG?HZ+Jaj}nz4HOsM&{~O5 zr@+GH<921*1}SKQCSj*}cT9kA8EB;-=&9=9t{#wFahm55wKEBD+VQgl!OrsN#Sqfy z>WZUGkcsTrtqxLHffbF=O3o6n>!hJN(;EcY*wkYSbFpvJI)khx$-XME@saX@@!}Lk zx54RXU!AOkOIr`OW_C%={y%fRqLiC7MBp7!8s(HhNnJavuDTI~V%rIHl{GEmw`>6| z4Wv7?f(8E7wn|oK7Cp4pUDACG>5!M|U~O?K;f4CUo7t7(J4du_BwfRS*!fA8Qa;RB z%%qPzY^w+ot(mF7vr6F6j6Myt)iPc>WXR38>my0$cpn)vZU(#HJ9O9&rWS-v4uT&Zv znwU|r=Z&wgOEp4sXj_hy9OFNN!ZTdjz-}OWHpIKvD-tE8IU7N%zi?6IyoKKUeiRzg zIJVlBsfGR)^KN>g+cnUAHcr!W=ym?NaW8al#`75C(U5$Xm|f#;>rVE7g(H2}2 zfWg*a|H5ab!{inG$piRqz}t8R*UPSozLmhHhpX?qDf{uu5jhfx3{V>P`of6&7n+!b zwQsB^j{V!RrU&Ax@ZBIkEb?HdW+d~|_hRRIKkdlH7J)04Dl#s~g1_wY6;U^vM~kmm znhb&6aYa?w+)KUA;koVgS{G>(3-k_R=tgk==>Q&eaJNH4c5woL3P1@dAxF$6;|u=H zE%MK+r4g;#-}a@bP0X46)En`d((EXHL<`=a;FEp^da8jIznwOC0y5xRt2Z(|9%uiU4R&z7cfw(qD{;=G|E}_}+y8K@|Ne^c1U)HK zU^EB$1pJubMg_w5^_LH9@_ed@B~H)}h}P`>>3J14O)hvwU3+i1DxJtsjV#ivs|{?+ zKEG3~y?bS=>T<0vG-hjbCQ%j(-Fd>vmWs(Kx&JuHbGA{Y5n3=6S3{dO3Kb zwu{SFHi!4`aSU8LM5i_(%OK!oCJv`K=_ciJ3aXs^_|j4nD*ke*qK5(+TrmlbTqn|W z+fo2lb~wr3W80LX0QQd~T_Yh9mRxhZ=1rHdA? zuOWlbtO;|K(qP~9TEEB}@hkfZzbm6ex@0^$ktQT8SVQN`xj|Ju+T{t$Q5n!?f)SQP zm*`ORSk;YszWUKH5$8ri56xUyH>bAV_%rv~yGSJ>$p;I9;;iNJluu7a+>+sCINm0y zZeVsvSM^HPo%O%;``{&#dM=@n62vj>3bBHn+zguJ$)_U4zbsj!|`^QLh*GR5qcSq^q=F@ZUMUdsXL1D5047SID3 zcsc~O4yzVDKm$3OD2sUcgd=xMDWB_^Gg+1$v7~Xm2;sPcuMoYEM2|KU;UZ22Fi7@} zFD$oVRZ`qqqXr)!o|kK!t#N{M^QIVaBC~X=8t;=mUrN1`4ZCBiib#<9xjySH>U0V^ERm`<0(q-7WN6}6Lsg_!i+XLH z8kfXumC;6yk57o*7u~l0a=1o~B)*+KbsBtcDEL$_`l6vB&HpFZ?CJ&`wk8|A9nLm;VR#xxF z=%s*Y!l;7*g_gJ>Y2U#~NIz|D{Ek?CwEPh?h%j3~s)lGryOHm9O`jrI+Eil z55{p^EJteM5h@3WFu)vvBjW3w7(nUdQNCKmk45&2E zGqWCzI~bD{>yT}dOBB`A^_vhgYfALPaK-d&$~?`Qa>8UKPdCF3b46y%v^|xhRYIJo z?)Tg4u7=tT=~|P93yD=Cj%%|&lovwdgW;s4*DpGgTt|%~YFKA|N6r36AJ-k$)bg}L zXrYG^xx*>`q!&hE@Pd1l@|i3F>jg)Q;zi;C-+v->x&4Xh@U$9`isZ>8S!2VO!A zkC1yd6l#6pl#F{vl-F-#M5s z*i<$Ol z=l3^0^|H%I6&jcD@3%B}th)nl)#WGFAlCiPeoaf=q>I?JW4D`f)S@z>s+*Ng3$*Cv z-8wnqZkl_gqa#auv^g&*nD@+dW=e+moJ zo-i+n8GB?PYa@)_wSLtq*{YY89h-0&zxe3ltcqY<%9yrt5q;x^0>M?~j3JyZh)fOU8aX7%!o_g%?DA6$)}^yeq8YARo%wCxq`?&KOvaSZU$S-)PeMpe9Vu#BKe zw80XfUD$28^vsaKY^yiV6HJBp-B)aXOm5ee&7n&qd>zipvo9#jGYmL+tokz41-N-rL>u^U_+-mU7LYA(9qze90y_%RF> z&}e2lSR&|ig2k?$4@(YR@_|2Saj`W_e>5EydL7uWSgi<%5NWa1>@_82e@BC!to~kQ z)?0)(O#`wV(7ZdmQ9|>hdxs(I0nbCs!f^9Kiit##au3icfg)hB@q?y182;2`eos-l0s_K{7iTLwThi{)X6bAg5 zOMftUUbn|hC#mEF+a_QfteZ<r3{|DL7z;TM#0^0%sHG{<`ywhU^d41K)f-}Vjj@8UPE~lLq=J^V`}9GM)!Zv&@;aj z0CK;Q8QHu;bYQo&t5j3at>lo*r4d4$pV3yDwZM@ou^7MMo- z3Wrke_o59c18u8nAruNgsUZn`xMA>S2L7EN=^+Yb0^=Cq6i%XtHCewKMgc(Id^Et0t5 z%yua$VNn5;xc2>VYoKDdT7D}_`057-Ao>;}>op5z=>rK$Rc)Sd&z5%{*Nvysde->z zs~}Z$Aut*ny@?}i$b(9+nfi?*EV*ZB5=Uz&N}5iJ1vpP!I5jJkH6oQUF1jJ}fk!G2 z=I;X4x6oGw-0oZEImE}S&$`5sD5xM{@PldwUHHq*=x257sCX`}7$QZCLq!_?3F3*- zl;jHpZ91Z>dwdE7ltt0l&$>MR0LrkmC!O${+SX6+Gc_{m*HY_vjBg}OD1zp~xJ1YE zP!ke&)wyU{+EXJ3`p4TD)g#gIFEHy>_^qMB1!{HcB6!gRk$RtM>h1#MG_L`iKWmMX zz>Ntsd~pWI?JRt;<#vBvA)G_KZ*KgihJnJ2iavO3H+h&+Tycx^xP6@f7?j8KIp#u^ z0*F~r*Nw%D144wv=1DbdWa&kItCm0|F_|gWh}5`I8bZ}IbR|B=jFaTF>sRho3#t-D zusU~`FyIjauhp=9oe#4_H0M(nBJAO7y8E(r@?ae<`32uxtASykGp^DB4 zry~e^-msW24#iKxzk*SqmE*9kC3rg>eCH{D@`KS*SJs5A@NKdnubV|L=yCvg*%p9n z_1E-Bh~8eP=K_5ljMNaw3F0~-T%9n12_T|*0FK{}$`%JB-^b6q>BNVPRfIL+E|7%9 zm8I(kQwJuRYS0cc2vcFw4WVD-mT^G5{t{!u^f~su8E&=VSCFJn-cQ2rnBcvEv{?t_ zrSt(YPYfX_l{HPEJ5B?5Q#ol@gXR9-Zshxctis+=!#7}F|ErY;t7a%+z$VQHk$`%O zx(8laHl#dWochR(OzIp3|Xr)*TG zM;B%zMudV@HwE@HLS|RHk_a^))27)uyW$jgh)?ukh9J9}i&YBRX+u)U#FZl!JST2h zSP-kJEJ|zhwY~v-OL`_=1$J^(-l1#h#|_z(W``L<*WmPsTJ?DT;QAmXF0evH2xEo4 z@C|l>YOsG4UX7FL5-HRq*1%pp?`awS{-Gz{9Gwc!B4Q-i((ZPtg&}C1u;*yd5D${$ zhNmY3qm8~saEXi`P4Ji{B((;t(^;sDOW8wuXHm^Pna|okw5Hc|%u&roSh|sylmZ|O zzyQLF5^1=Ct!R93R4Cn{jCn>$Nr^i%lc4%FXsHyzYnF#k<`ef1J@TnPfRQ3=Cdmtr za_Fq5!Pn18M8b(w35sKb;SM3G*0ZLp==Y>LB_N$uVf zTIk;4JPZmD?5=3`Ye1FLWMs*$!Xt(+z_|4VC>k}Xhs@!cPGHOqIm6^|3T&oCZSga* zkOHNwU>IdZrAsP}dFO#sNLd7I6B1cA736NE%@;~VLVQaaeaKqBKFmf8Y9$kwPJmeX z<(tMQ-l_s)VO}s2Z)GwoS`Q8?rxqkO@gtnG!b{{xl+W1cR)}D$FPUj6>X?Dj_qbwK zr9|EqZ>@b>4My;c6oYK zphWRs3}Zc|!hDuSTX@tw7a9sSdn6If1M7Q>%R_Y#7e)xQ znR;^0{IH8GBo-njkTKQ8&@vQy zjx%}jNz2S3#Rxutx{R=C3e?&dtEjkVHPc7DZf4hBS5L`~mn!Ow+mMXLG)BR#h*sq? z0i}2@f!6B^TZIawZ7b&Bj!uU;&sdRw<9b470C9kONa+nm=|LS`HxZ5wi-~o%>rQEy zhA?dX_ni>T$6V~O|J^#T9BGG(^}8qMdoDNg7vC%H6g{8%+~KiBLyT!MYH|{ZHSkPt z-XSat;Wq-*Smc9mI~8h>atXAb&~i{K_O*oMwCA#Gz=3_}rHq-Gqr6 z5#H7%AR8OyB;!hCYtU^PQ?u(LM+WNWWzk?G5Nq5FBZ~bc63g=*jCmIxQd;q-v8?pi zFuV$y%Li`?#~t2B?492#C`bMM*C+zX-FNtj8XzrrI^l)%_`PWEWP?RSAqB}*rhK{O zTM@&&QTUJb#e;ZDbjE=kM{fx|VMF9R`L31)SHv@2BfsZEkt0kG09iBEHpMZIIEnvW0al{{|CK!gl z91Bq<_D1BAA?^4%Isq@;sh5DQA_WC>oOh|iWn>5;_z!tWl28d5k^MCUAb?flAO`Tj zZ3e&SBcTju44Nx2sunSVAFNO+YZiS64^5&Eslw$gH-%!9>05`EKzg%yaCoyc;0hL; zXB=HeLAtjPe~ldHH93fxgk?)4>n$LoOTu$y-2Gs| z>&=mc!7q>*GAg$Ph(D>~PC#HpR0!>L(rkQ!N8tjFic5m15NpquxKwWScXWMI^gvFe zjUM3Mjb|WFxTYegf(Oz(^vu2cWiu{on}GQ71dR!KGzj~FE`~pJJ;+*HqKMHG4{kMM z0Ua_|=#OHIxX>yMm{3)?X9o7?4*Z_V-!83y-^3Ky7Sbp_yF$;zZwU3fGi$G-zYsZl zrM20R{iUPG&_p`ILtg|d%9xdC+ltHx!z+0I5_1igf6mF>y44oGY};hQ;()|OHdQns zvuspUX(%3zpR~uy;uayU@}{xAa$s_QKlz8Nc;oe}3{naE`_Bh2)%sIiy}Ed;q}oOh z(Bc{_)xT}A+>0yk5s|I+@MHIXBOAvyA>aDEi!AW&oZpr9h>DaTht$dY4vKqJssuh< z9boA^#ALNle)oH3+i|~p_k;ef7{VVH$`JXnogjGgT4_}9=CqDx6F{y#fRJIbS5U<{ zO?}GP6v;6AxX(HEDUm~l6vCd$-Fkg`nl?Dlw2%yj?FJ4`b1Q20ww@18t&M8p=jgbp zm@5VkD~V=T2W}kN^Z@UkxY~Rsg8}u0OUB13?@&=l=-%WLl_qSP-eix z=6hFXbXjBzE?`zamT&fh=sT_+LpwfI94&PG_*qo<-#l-p-&+49 z=`(e4K2y0p9%Z@Pg1hd4WA-~`_4_@}!OOu#f)8KK&g zt3-7%E|(eRr8TqSujWa99v{|yQxvA3>Waq7MKlyHTRo?Iu<)c$AvrHg_M)Tjk!0zC zuet0)WZB5bxoj!-Z{&A&vAQ0!dvNi>LQ@F$JEDv6y7rf~3`7aFSsfk6rw$;s$1{3f zPi!^MnOuSgq#0sBpR>I$jf<-YxE{$v}?tMG+&yBpCo?!-b5Hto36L7W@kM0{;zFpkQ|uIAjmiHl;h{n?~Ph(jM|M!&BP zY0EBC!Ri_7En3(6O7lKLuSVG_J>7vjbuEwqpB7zi7nQyw9UDd)%NE)R&>&_dKviMfXqmm$Yjc0*h^ElWu^W$a(?)l?+;T|2rb=G#B#*a_2J(mK^%0%l6{j-{$Q-^BLL=dV zO*O%o1yi>_w;xsA7*I*#zA$phsGBe$<`tF1tE^nKZM##&m!Yo%MMl2*PDaAkGu=7c z#{`Sp*%T2q%p^DMZ45_QTdWWbxqa0^9zm0tFLc=>XTQHROpk>%7ITZdF$FSIDyb6^ zxYb(nU>nC~a%l|T1gC4*FrqRpU9$mQanGEim2DI`;&>gUXMYJIRzTr&o~Dc{#c+Zg9f2dzeu!guzp+ohe8sDB+d3q8vgr5z$&~!xf};K={qwh?j~w z#o)D^P{=~<2NdX=nQ^Uu=o5r__|ft1aqn^Od3F#0g$F2qa}lMc^ENPRNA& z(Pz-PWk!%I-1G+9+dH?3OfjKC6DHBcnT40hc|Y^%s2&U& zFuo&OLFO%`i@69^gwc{Y_f*!|XUW4}TlDozh6$xL~Gl<$okPrvhDY1lV- zh4AntA`|d$G;7alrIg~mmAsC0U|5=y|Nr?daO@QT3lAITv)sUV92{)NxNEbxQ*p|H z@oS9G>Bu7Vqz1kSIAnux8rtnIH7>yM(b-LTB~#5m5SlhgF?sapYL)-}hXJoki|aN$ zMD2R~Jm2|xdOh^cPITEMO*Wzk!cev!YB%kCwYp8lA?Eu~X)|v|nMv})V%uQ^(Sm5= zeU=J7AvxGe+I(iB+5UpC2^4)HQ<5v}`uk6v;l6R_I*o2jzxy_TpC12wgqhI6saSDM zyUh7y{8DAnpcmV>rugLuhzIUx)YOYe!V)NxgB zBxTTyqt~Q#t$g6U2RB5BD`na2{f-P}y>4xXcFWSVOJDKYeX4x;NanTA6RHmkXjcF3 zrJsJ|zr)QbgYxe%@?SRfl9%zt;70M`9Ps)782wM^`Dx~m-lj;P2szQr+nGy=6PZ2U z;P}tpX7red*E3aKqGD|h2I+iYuxQ@V<&0YSMq&( z9JksgrCQzu<11$+@TGNJH7DE{T%8mHA=jNIiQ`CBxkgfIq~1!cNj%s2G>THjc&e{)PGoIz*%IIR#oLY;7 z(1DjdLn28Yz6?x~PNHDZ=Z*c-2<^;#3A$GtnllcU7-dF&oPszfQkfBH`{qyPSZ?g3 zG+pkZA$=aaMM+v6()ui6_0^Ui3%gC;-Itb{t(%QLXw6cNMY+J-q>oo%k8aKf5knhV zf!K9-?~eR?AWC9WpVCe&iAI@d>Sec;o!H<7`2C6XS2?6S2;pm_T)H=lwkzI%c&b3d z+UNQy_HN1WJMC0{f=;f{PQ6Hf0l6hcy=7!NH{+4iH%{gBTD7!dMTvQF9b<>|H*+L; z-<6avu-#s{{V<&Q8k3p{v8QAHJxRkVnC`byuHxesi<@?T~M^2+K#@qqXlgAJJsVb+Wb zoHIj>0f0~S4D<0{WoC9Oe)N~PP{w2Gab>)^ns`7uz;BZEvv&t~g8+B{cHG~u=Kp!G zehK(*A9L!hf3;I@!)E%oUp_nOS?_&{`8BE1KbW7V=zmT2ONCSD>12oc|8{Tx3wJvD zS3B)FnEe~~_o@HWMrR%4Df*YXDgS}~(@maT`q?M@-?BIi{;kPBQqBrE`-_)bMBu+X1KTKKlkZb=Cj|k e+W!gs3IF+8sjEr&(|tj3RB22&)go`4g8mPO!Q{^X literal 0 HcmV?d00001 diff --git a/enlish-service/src/test/java/com/yinlihupo/enlish/service/omr/TestOmr.java b/enlish-service/src/test/java/com/yinlihupo/enlish/service/omr/TestOmr.java index 700a7ac..f075525 100644 --- a/enlish-service/src/test/java/com/yinlihupo/enlish/service/omr/TestOmr.java +++ b/enlish-service/src/test/java/com/yinlihupo/enlish/service/omr/TestOmr.java @@ -1,28 +1,20 @@ package com.yinlihupo.enlish.service.omr; -import com.yinlihupo.enlish.service.enums.AssessmentsType; import com.yinlihupo.enlish.service.model.bo.CoordinatesXY; -import com.yinlihupo.enlish.service.model.bo.Word; -import com.yinlihupo.enlish.service.service.AssessmentService; import com.yinlihupo.enlish.service.utils.PngUtil; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import nu.pattern.OpenCV; import org.junit.jupiter.api.Test; -import org.opencv.core.*; import org.springframework.boot.test.context.SpringBootTest; import java.util.Arrays; -import java.util.HashMap; import java.util.List; @SpringBootTest @Slf4j public class TestOmr { - @Resource - private AssessmentService assessmentService; - @Test public void testOmr(){ OpenCV.loadLocally(); @@ -38,28 +30,7 @@ public class TestOmr { String path = "C:\\project\\java\\enlish_edu\\enlish\\enlish-service\\src\\main\\resources\\templates\\p3.png"; List coordinatesXIES = PngUtil.analysisXY(path); - for (CoordinatesXY coordinatesXY : coordinatesXIES) { - log.info("坐标: {}", coordinatesXY); - } - Integer studentId = 1; - HashMap unitIdAndWordCount = new HashMap<>(); - unitIdAndWordCount.put(142, 30); - unitIdAndWordCount.put(132, 40); - - Integer assessmentDocxId = assessmentService.getAssessmentDocxId(studentId, AssessmentsType.ASSESSMENT_FELT); - List assessmentWords; - if (assessmentDocxId == null || assessmentDocxId == 0) { - assessmentWords = assessmentService.genAssessmentWords(studentId, unitIdAndWordCount); - } else { - assessmentWords = assessmentService.getAssessmentWordsById(assessmentDocxId); - } - - List integers = PngUtil.analyzePngForUnmemorizedWordIds(path, assessmentWords, coordinatesXIES); - - for (Integer integer : integers) { - log.info("未背熟的单词ID: {}", integer); - } } diff --git a/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/assessmentTest.java b/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/assessmentTest.java deleted file mode 100644 index 609fac6..0000000 --- a/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/assessmentTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.yinlihupo.enlish.service.service; - -import com.deepoove.poi.XWPFTemplate; -import com.deepoove.poi.config.Configure; -import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy; -import com.yinlihupo.enlish.service.enums.AssessmentsType; -import com.yinlihupo.enlish.service.model.bo.Word; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -import java.io.FileOutputStream; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@SpringBootTest -public class assessmentTest { - - @Resource - private AssessmentService assessmentService; - - @Test - public void generateAssessmentDocxStudent() { - - Integer studentId = 1; - HashMap unitIdAndWordCount = new HashMap<>(); - unitIdAndWordCount.put(142, 30); - unitIdAndWordCount.put(132, 40); - - Integer assessmentDocxId = assessmentService.getAssessmentDocxId(studentId, AssessmentsType.ASSESSMENT_FELT); - List assessmentWords; - if (assessmentDocxId == null || assessmentDocxId == 0) { - assessmentWords = assessmentService.genAssessmentWords(studentId, unitIdAndWordCount); - assessmentDocxId = assessmentService.getAssessmentDocxId(studentId, AssessmentsType.ASSESSMENT_FELT); - } else { - assessmentWords = assessmentService.getAssessmentWordsById(assessmentDocxId); - } - - LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); - Configure config = Configure.builder() - .bind("words", policy) - .build(); - - Map data = new HashMap<>(); - data.put("assessment_id", assessmentDocxId); - data.put("words", assessmentWords); - - // 4. 渲染并输出 - try (XWPFTemplate template = XWPFTemplate.compile("C:\\project\\java\\enlish_edu\\enlish\\enlish-service\\src\\main\\resources\\templates\\assessment_v4.docx", config)) { - template.render(data); - template.write(new FileOutputStream("学生单词测试卷.docx")); - System.out.println("文档生成成功!"); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamTest.java b/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamTest.java new file mode 100644 index 0000000..bc97766 --- /dev/null +++ b/enlish-service/src/test/java/com/yinlihupo/enlish/service/service/exam/ExamTest.java @@ -0,0 +1,57 @@ +package com.yinlihupo.enlish.service.service.exam; + +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 com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; +import com.yinlihupo.enlish.service.model.bo.Word; +import com.yinlihupo.enlish.service.service.ExamWordsService; +import com.yinlihupo.enlish.service.service.VocabularyService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +import java.io.FileOutputStream; +import java.util.*; + +@SpringBootTest +@Slf4j +public class ExamTest { + + @Resource + private ExamWordsService examWordsService; + @Resource + private VocabularyService vocabularyService; + @Test + public void test() { + ExamWordsDO examWordsDO = examWordsService.generateExamWords(5, 0, List.of(1)); + log.info("{}", examWordsDO); + List vocabularyBankDOS = vocabularyService.findVocabularyBankDOListById(examWordsDO.getWordIds()); + List assessmentWords = vocabularyBankDOS.stream().map(vocabularyBankDO -> Word.builder() + .id(vocabularyBankDO.getId()) + .title(vocabularyBankDO.getWord()) + .definition(vocabularyBankDO.getDefinition()) + .build()).toList(); + + + LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy(); + Configure config = Configure.builder() + .bind("words", policy) + .build(); + + Map data = new HashMap<>(); + data.put("id", examWordsDO.getId()); + data.put("words", assessmentWords); + + // 4. 渲染并输出 + try (XWPFTemplate template = XWPFTemplate.compile("C:\\project\\java\\enlish_edu\\enlish\\enlish-service\\src\\main\\resources\\templates\\assessment_v5.docx", config)) { + template.render(data); + template.write(new FileOutputStream("学生单词测试卷.docx")); + System.out.println("文档生成成功!"); + } catch (Exception e) { + e.printStackTrace(); + } + } +}