From d777437e82dc4d2474d1d8059a1d3d5bce98cfb8 Mon Sep 17 00:00:00 2001 From: lbw <1192299468@qq.com> Date: Wed, 10 Dec 2025 16:41:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E7=94=9F=E6=88=90=E8=AF=84=E6=B5=8Bdocx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- enlish-service/pom.xml | 5 + .../controller/AssessmentController.java | 77 +++++++++ .../domain/dataobject/AssessmentsDO.java | 32 ++++ .../domain/dataobject/WordMasteryLogDO.java | 25 +++ .../domain/mapper/AssessmentsDOMapper.java | 24 +++ .../domain/mapper/VocabularyBankDOMapper.java | 5 + .../domain/mapper/WordMasteryLogDOMapper.java | 12 ++ .../enlish/service/enums/AssessmentsType.java | 15 ++ .../enlish/service/model/bo/Word.java | 19 +++ .../enlish/service/model/bo/package-info.java | 2 + .../enlish/service/model/package-info.java | 1 + .../vo/assessment/AssessmentStudentReqVO.java | 27 +++ .../service/service/AssessmentService.java | 17 ++ .../assessment/AssessmentServiceImpl.java | 82 +++++++++ .../src/main/resources/generatorConfig.xml | 2 +- .../resources/mapper/AssessmentsDOMapper.xml | 156 ++++++++++++++++++ .../mapper/VocabularyBankDOMapper.xml | 7 + .../mapper/WordMasteryLogDOMapper.xml | 29 ++++ .../main/resources/templates/assessment.docx | Bin 0 -> 12327 bytes pom.xml | 6 + 20 files changed, 542 insertions(+), 1 deletion(-) create 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/domain/dataobject/AssessmentsDO.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/WordMasteryLogDO.java create 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/WordMasteryLogDOMapper.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/enums/AssessmentsType.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/Word.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/package-info.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/model/package-info.java create 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/service/AssessmentService.java create mode 100644 enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java create mode 100644 enlish-service/src/main/resources/mapper/AssessmentsDOMapper.xml create mode 100644 enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml create mode 100644 enlish-service/src/main/resources/templates/assessment.docx diff --git a/enlish-service/pom.xml b/enlish-service/pom.xml index 2b47dd8..92963e1 100644 --- a/enlish-service/pom.xml +++ b/enlish-service/pom.xml @@ -90,6 +90,11 @@ log4j-core + + com.deepoove + poi-tl + + 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 new file mode 100644 index 0000000..5248e61 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/controller/AssessmentController.java @@ -0,0 +1,77 @@ +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.enums.AssessmentsType; +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 jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +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.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); + } + + + } +} 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/AssessmentsDO.java new file mode 100644 index 0000000..ddfa26b --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/AssessmentsDO.java @@ -0,0 +1,32 @@ +package com.yinlihupo.enlish.service.domain.dataobject; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class AssessmentsDO { + + private Integer id; + + private Integer studentId; + + private LocalDateTime testDate; + + private Integer testType; + + private Integer level; + + private Integer estimatedVocabSize; + + private String contentDetailsJson; + +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/WordMasteryLogDO.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/WordMasteryLogDO.java new file mode 100644 index 0000000..48ff77a --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/dataobject/WordMasteryLogDO.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.math.BigDecimal; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class WordMasteryLogDO { + private Integer id; + + private Integer studentId; + + private Integer wordId; + + private Integer reviewCount; + + private BigDecimal memoryStrength; + +} \ 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 new file mode 100644 index 0000000..5db630e --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/AssessmentsDOMapper.java @@ -0,0 +1,24 @@ +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/VocabularyBankDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/VocabularyBankDOMapper.java index db03dd2..4418c3f 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 @@ -1,6 +1,9 @@ package com.yinlihupo.enlish.service.domain.mapper; import com.yinlihupo.enlish.service.domain.dataobject.VocabularyBankDO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; public interface VocabularyBankDOMapper { int deleteByPrimaryKey(Integer id); @@ -14,4 +17,6 @@ public interface VocabularyBankDOMapper { int updateByPrimaryKeySelective(VocabularyBankDO record); int updateByPrimaryKey(VocabularyBankDO record); + + List selectVocabularyBankDOListByUnitId(@Param("unitId") Integer unitId, @Param("wordCount") Integer wordCount); } \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java new file mode 100644 index 0000000..b06d58a --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/domain/mapper/WordMasteryLogDOMapper.java @@ -0,0 +1,12 @@ +package com.yinlihupo.enlish.service.domain.mapper; + +import com.yinlihupo.enlish.service.domain.dataobject.WordMasteryLogDO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface WordMasteryLogDOMapper { + + // 查询指定学生指定单元的词,根据记忆强度排序 + List selectByStudentIdAndUnitIdOrderByMemoryStrength(@Param("studentId") Integer studentId, @Param("unitId") Integer unitId, @Param("limit") Integer limit); +} \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/enums/AssessmentsType.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/enums/AssessmentsType.java new file mode 100644 index 0000000..c2bfd7b --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/enums/AssessmentsType.java @@ -0,0 +1,15 @@ +package com.yinlihupo.enlish.service.enums; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum AssessmentsType { + + ASSESSMENT_FELT(1, "摸底测试"); + + private final Integer id; + private final String name; +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/Word.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/Word.java new file mode 100644 index 0000000..26746cb --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/Word.java @@ -0,0 +1,19 @@ +package com.yinlihupo.enlish.service.model.bo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +@Builder +public class Word { + + private Integer id; + + private String title; + + private String definition; +} diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/package-info.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/package-info.java new file mode 100644 index 0000000..4cc348a --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/bo/package-info.java @@ -0,0 +1,2 @@ +// 中间层:业务对象 +package com.yinlihupo.enlish.service.model.bo; \ No newline at end of file diff --git a/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/package-info.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/package-info.java new file mode 100644 index 0000000..a9d61b6 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/package-info.java @@ -0,0 +1 @@ +package com.yinlihupo.enlish.service.model; \ 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 new file mode 100644 index 0000000..00f4460 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/model/vo/assessment/AssessmentStudentReqVO.java @@ -0,0 +1,27 @@ +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/service/AssessmentService.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/AssessmentService.java new file mode 100644 index 0000000..abae1df --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/AssessmentService.java @@ -0,0 +1,17 @@ +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/assessment/AssessmentServiceImpl.java b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java new file mode 100644 index 0000000..2950db4 --- /dev/null +++ b/enlish-service/src/main/java/com/yinlihupo/enlish/service/service/assessment/AssessmentServiceImpl.java @@ -0,0 +1,82 @@ +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/resources/generatorConfig.xml b/enlish-service/src/main/resources/generatorConfig.xml index 98b5ad0..fa7fa14 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 + + + + select + + , + + from assessments + where id = #{id,jdbcType=INTEGER} + + + + 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} + + + + select id + from assessments + where student_id = #{studentId,jdbcType=INTEGER} + and test_type = #{testType,jdbcType=INTEGER} + + + + select content_details_json + from assessments + where id = #{assessmentsId,jdbcType=INTEGER} + + + \ 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 4e17f8e..4616f99 100644 --- a/enlish-service/src/main/resources/mapper/VocabularyBankDOMapper.xml +++ b/enlish-service/src/main/resources/mapper/VocabularyBankDOMapper.xml @@ -97,4 +97,11 @@ where id = #{id,jdbcType=INTEGER} + + select * + from vocabulary_bank + where unit_id = #{unitId} + limit #{wordCount} + + \ No newline at end of file diff --git a/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml b/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml new file mode 100644 index 0000000..98fcbff --- /dev/null +++ b/enlish-service/src/main/resources/mapper/WordMasteryLogDOMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + id, student_id, word_id, review_count, memory_strength + + + + select * + from word_mastery_log + where student_id = #{studentId} + and word_id in ( + select id + from vocabulary_bank + where unit_id = #{unitId} + ) + order by memory_strength desc + limit #{limit} + + + \ No newline at end of file diff --git a/enlish-service/src/main/resources/templates/assessment.docx b/enlish-service/src/main/resources/templates/assessment.docx new file mode 100644 index 0000000000000000000000000000000000000000..04586968a49aa25e60676fb262204f98da8d50ad GIT binary patch literal 12327 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.20.0 + + com.deepoove + poi-tl + 1.12.1 + + org.projectlombok
2.20.0 + + com.deepoove + poi-tl + 1.12.1 + + org.projectlombok