feat(student-plan): 实现学生学案查询功能
- 新增FindStudentPlansReqVO和FindStudentPlansRspVO定义请求和响应数据结构 - 新增LessonPlanItem用于描述单个学案项 - StudentLessonPlansDO模型新增isFinished属性 - 扩展StudentLessonPlansDOMapper,添加分页及按姓名查询学生学案列表方法及统计总数方法 - 扩展LessonPlansDOMapper,新增按学案ID列表批量查询方法 - 实现StudentLessonPlansService及LessonPlansService接口对应查询方法 - 新增StudentLessonPlansController,提供学生学案分页查询接口 - 在前端LearningPlan.vue添加学案查询界面及分页、搜索功能 - 封装studentLessonPlans接口axios方法,支持分页按姓名查询学生学案数据 - 添加单元测试更新验证数据库查询正确性
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package com.yinlihupo.enlish.service.controller;
|
||||
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.LessonPlansDO;
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentLessonPlansDO;
|
||||
import com.yinlihupo.enlish.service.model.bo.StudentDetail;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.FindStudentPlansReqVO;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.FindStudentPlansRspVO;
|
||||
import com.yinlihupo.enlish.service.model.vo.plan.LessonPlanItem;
|
||||
import com.yinlihupo.enlish.service.service.LessonPlansService;
|
||||
import com.yinlihupo.enlish.service.service.StudentLessonPlansService;
|
||||
import com.yinlihupo.enlish.service.service.StudentService;
|
||||
import com.yinlihupo.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||
import com.yinlihupo.framework.common.response.PageResponse;
|
||||
import jakarta.annotation.Resource;
|
||||
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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RequestMapping("/studentLessonPlans/")
|
||||
@RestController
|
||||
public class StudentLessonPlansController {
|
||||
|
||||
@Resource
|
||||
private StudentLessonPlansService studentLessonPlansService;
|
||||
@Resource
|
||||
private StudentService studentService;
|
||||
@Resource
|
||||
private LessonPlansService lessonPlansService;
|
||||
|
||||
@PostMapping("/list")
|
||||
@ApiOperationLog(description = "查询学生学案")
|
||||
public PageResponse<FindStudentPlansRspVO> findStudentPlans(@RequestBody FindStudentPlansReqVO findStudentPlansReqVO) {
|
||||
|
||||
Integer studentLessonPlanTotal = studentLessonPlansService.findStudentLessonPlanTotal();
|
||||
|
||||
String name = findStudentPlansReqVO.getName();
|
||||
Integer page = findStudentPlansReqVO.getPage();
|
||||
Integer size = findStudentPlansReqVO.getSize();
|
||||
|
||||
List<StudentLessonPlansDO> studentLessonPlansDOListPageSize = studentLessonPlansService.findStudentLessonPlansDOList(page, size, name);
|
||||
Map<Integer, List<StudentLessonPlansDO>> studentId2StudentLessonPlansDOListMap = studentLessonPlansDOListPageSize.stream().collect(
|
||||
Collectors.groupingBy(StudentLessonPlansDO::getStudentId)
|
||||
);
|
||||
|
||||
List<Integer> planIds = studentLessonPlansDOListPageSize.stream().map(StudentLessonPlansDO::getPlanId).toList();
|
||||
List<LessonPlansDO> lessonPlans = lessonPlansService.findLessonPlans(planIds);
|
||||
Map<Integer, LessonPlansDO> id2LessonPlansDO = lessonPlans.stream().collect(Collectors.toMap(
|
||||
LessonPlansDO::getId,
|
||||
lessonPlansDO -> lessonPlansDO
|
||||
));
|
||||
|
||||
List<StudentDetail> studentDetailList = studentService.getStudentDetailList(new ArrayList<>(studentId2StudentLessonPlansDOListMap.keySet()));
|
||||
|
||||
List<FindStudentPlansRspVO> findStudentPlansRspVOList = studentDetailList.stream().map(studentDetail -> {
|
||||
List<StudentLessonPlansDO> studentLessonPlansDOList = studentId2StudentLessonPlansDOListMap.get(studentDetail.getId());
|
||||
return FindStudentPlansRspVO.builder()
|
||||
.name(studentDetail.getName())
|
||||
.id(studentDetail.getId())
|
||||
.classId(studentDetail.getClassId())
|
||||
.gradeId(studentDetail.getGradeId())
|
||||
.gradeName(studentDetail.getGradeName())
|
||||
.className(studentDetail.getClassName())
|
||||
.plans(studentLessonPlansDOList.stream().map(studentLessonPlansDO ->
|
||||
LessonPlanItem.builder().title(id2LessonPlansDO.get(studentLessonPlansDO.getPlanId()).getTitle()).id(studentLessonPlansDO.getPlanId()).isFinished(studentLessonPlansDO.getIsFinished()).build()
|
||||
).toList())
|
||||
.build();
|
||||
}).toList();
|
||||
|
||||
return PageResponse.success(findStudentPlansRspVOList, page, studentLessonPlanTotal, size);
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,8 @@ public class StudentLessonPlansDO {
|
||||
|
||||
private Integer totalCount;
|
||||
|
||||
private Integer isFinished;
|
||||
|
||||
private String memorizedWordsJson;
|
||||
|
||||
private String unmemorizedWordsJson;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package com.yinlihupo.enlish.service.domain.mapper;
|
||||
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.LessonPlansDO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface LessonPlansDOMapper {
|
||||
|
||||
void insert(LessonPlansDO lessonPlansDO);
|
||||
|
||||
LessonPlansDO selectById(Integer id);
|
||||
|
||||
List<LessonPlansDO> findLessonPlansByStudentId(@Param("ids") List<Integer> ids);
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
package com.yinlihupo.enlish.service.domain.mapper;
|
||||
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentLessonPlansDO;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StudentLessonPlansDOMapper {
|
||||
|
||||
void insert(StudentLessonPlansDO studentLessonPlansDO);
|
||||
|
||||
List<StudentLessonPlansDO> selectStudentLessonPlanList(@Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize, @Param("name") String name);
|
||||
|
||||
Integer selectStudentPlanTotal();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.yinlihupo.enlish.service.model.vo.plan;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class FindStudentPlansReqVO {
|
||||
|
||||
String name;
|
||||
Integer page;
|
||||
Integer size;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.yinlihupo.enlish.service.model.vo.plan;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class FindStudentPlansRspVO {
|
||||
|
||||
private Integer id;
|
||||
private String name;
|
||||
private Integer classId;
|
||||
private String className;
|
||||
private Integer gradeId;
|
||||
private String gradeName;
|
||||
|
||||
List<LessonPlanItem> plans;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.yinlihupo.enlish.service.model.vo.plan;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
@Builder
|
||||
public class LessonPlanItem {
|
||||
|
||||
private Integer id;
|
||||
|
||||
private String title;
|
||||
|
||||
private Integer isFinished;
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
package com.yinlihupo.enlish.service.service;
|
||||
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.LessonPlansDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface LessonPlansService {
|
||||
void generateLessonPlans(Integer studentId, Integer unitId);
|
||||
|
||||
List<LessonPlansDO> findLessonPlans(List<Integer> ids);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.yinlihupo.enlish.service.service;
|
||||
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentLessonPlansDO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface StudentLessonPlansService {
|
||||
|
||||
List<StudentLessonPlansDO> findStudentLessonPlansDOList(Integer page, Integer size, String name);
|
||||
|
||||
Integer findStudentLessonPlanTotal();
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import com.yinlihupo.enlish.service.utils.DifyArticleClient;
|
||||
import com.yinlihupo.enlish.service.utils.StringToPlanMapUtil;
|
||||
import com.yinlihupo.framework.common.util.JsonUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -150,6 +149,10 @@ public class LessonPlansServiceImpl implements LessonPlansService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LessonPlansDO> findLessonPlans(List<Integer> ids) {
|
||||
return lessonPlansDOMapper.findLessonPlansByStudentId(ids);
|
||||
}
|
||||
|
||||
|
||||
private Map<String, Object> generateWeekendPlans(List<VocabularyBankDO> checkList,
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.yinlihupo.enlish.service.service.plan;
|
||||
|
||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentLessonPlansDO;
|
||||
import com.yinlihupo.enlish.service.domain.mapper.StudentLessonPlansDOMapper;
|
||||
import com.yinlihupo.enlish.service.service.StudentLessonPlansService;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class StudentLessonPlansServiceImpl implements StudentLessonPlansService {
|
||||
|
||||
@Resource
|
||||
private StudentLessonPlansDOMapper studentLessonPlansDOMapper;
|
||||
|
||||
|
||||
@Override
|
||||
public List<StudentLessonPlansDO> findStudentLessonPlansDOList(Integer page, Integer size, String name) {
|
||||
log.info("查询学生学案");
|
||||
if (name.isEmpty()) {
|
||||
return studentLessonPlansDOMapper.selectStudentLessonPlanList((page - 1) * size, size, null);
|
||||
}
|
||||
return studentLessonPlansDOMapper.selectStudentLessonPlanList((page - 1) * size, size, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer findStudentLessonPlanTotal() {
|
||||
return studentLessonPlansDOMapper.selectStudentPlanTotal();
|
||||
}
|
||||
}
|
||||
@@ -22,4 +22,15 @@
|
||||
where id = #{id}
|
||||
</select>
|
||||
|
||||
<select id="findLessonPlansByStudentId" resultMap="BaseResultMap">
|
||||
select *
|
||||
from lesson_plans
|
||||
<if test="ids != null">
|
||||
where id in
|
||||
<foreach item="id" collection="ids" separator="," open="(" close=")" index="">
|
||||
#{id}
|
||||
</foreach>
|
||||
</if>
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -8,6 +8,7 @@
|
||||
<result column="start_time" jdbcType="TIMESTAMP" property="startTime" />
|
||||
<result column="mastery_rat" jdbcType="DECIMAL" property="masteryRat" />
|
||||
<result column="total_count" jdbcType="INTEGER" property="totalCount" />
|
||||
<result column="is_finished" jdbcType="INTEGER" property="isFinished" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.yinlihupo.enlish.service.domain.dataobject.StudentLessonPlansDO">
|
||||
@@ -22,4 +23,26 @@
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="selectStudentLessonPlanList" resultMap="BaseResultMap">
|
||||
SELECT slp.*
|
||||
FROM student_lesson_plans slp
|
||||
LEFT JOIN student s ON slp.student_id = s.id
|
||||
JOIN (
|
||||
SELECT DISTINCT slp_in.student_id
|
||||
FROM student_lesson_plans slp_in
|
||||
LEFT JOIN student s_in ON slp_in.student_id = s_in.id
|
||||
<if test="name != null and name != ''">
|
||||
WHERE s_in.name LIKE CONCAT('%', #{name}, '%')
|
||||
</if>
|
||||
ORDER BY slp_in.student_id
|
||||
LIMIT #{startIndex}, #{pageSize}
|
||||
) AS temp ON slp.student_id = temp.student_id
|
||||
ORDER BY slp.id
|
||||
</select>
|
||||
|
||||
<select id="selectStudentPlanTotal">
|
||||
SELECT count(DISTINCT student_id) AS total
|
||||
FROM student_lesson_plans;
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -17,7 +17,7 @@ public class PlanTest {
|
||||
|
||||
@Test
|
||||
public void test() throws Exception {
|
||||
LessonPlansDO lessonPlansDO = lessonPlansDOMapper.selectById(21);
|
||||
LessonPlansDO lessonPlansDO = lessonPlansDOMapper.selectById(58);
|
||||
Map<String, Object> stringObjectMap = JsonUtils.parseMap(lessonPlansDO.getContentDetails(), String.class, Object.class);
|
||||
for (Map.Entry<String, Object> entry : stringObjectMap.entrySet()) {
|
||||
System.out.println(entry.getKey());
|
||||
|
||||
9
enlish-vue/src/api/studentLessonPlans.js
Normal file
9
enlish-vue/src/api/studentLessonPlans.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import axios from "@/axios";
|
||||
|
||||
export function findStudentLessonPlans(page, size, name) {
|
||||
return axios.post('/studentLessonPlans/list', {
|
||||
page: page,
|
||||
size: size,
|
||||
name: name ?? ''
|
||||
})
|
||||
}
|
||||
@@ -6,10 +6,44 @@
|
||||
</el-header>
|
||||
|
||||
<el-main class="p-4">
|
||||
<el-tabs v-model="activeTab" type="border-card" class="demo-tabs">
|
||||
<el-tab-pane label="学习计划" name="first">学习计划</el-tab-pane>
|
||||
<el-tab-pane label="学习记录" name="second">学习记录</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||
<div class="text-lg font-semibold mb-4">学案查询</div>
|
||||
<div class="flex flex-wrap items-center gap-3 mb-4">
|
||||
<el-input v-model="searchName" placeholder="按姓名查询" clearable style="max-width: 220px" />
|
||||
<el-button type="primary" @click="onSearch">查询</el-button>
|
||||
<el-button @click="onReset">重置</el-button>
|
||||
</div>
|
||||
<el-table ref="tableRef" :data="rows" border class="w-full" v-loading="loading" row-key="id">
|
||||
<el-table-column type="expand">
|
||||
<template #default="{ row }">
|
||||
<div class="p-3">
|
||||
<div class="text-sm font-semibold mb-2">学案</div>
|
||||
<el-table :data="row.plans || []" size="small" border>
|
||||
<el-table-column prop="id" label="学案ID" width="100" />
|
||||
<el-table-column prop="title" label="标题" min-width="280" />
|
||||
<el-table-column label="状态" width="120">
|
||||
<template #default="{ row: plan }">
|
||||
<el-tag :type="plan.isFinished === 1 ? 'success' : 'info'"
|
||||
effect="plain">
|
||||
{{ plan.isFinished === 1 ? '已完成' : '未完成' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="id" label="学生ID" width="100" />
|
||||
<el-table-column prop="name" label="姓名" min-width="120" />
|
||||
<el-table-column prop="className" label="班级" min-width="120" />
|
||||
<el-table-column prop="gradeName" label="年级" min-width="120" />
|
||||
</el-table>
|
||||
<div class="mt-4 flex justify-end">
|
||||
<el-pagination background layout="prev, pager, next, sizes, total" :total="totalCount"
|
||||
:page-size="pageSize" :current-page="pageNo" @current-change="handlePageChange"
|
||||
@size-change="handleSizeChange" />
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
|
||||
</el-container>
|
||||
@@ -18,5 +52,52 @@
|
||||
|
||||
<script setup>
|
||||
import Header from '@/layouts/components/Header.vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { findStudentLessonPlans } from '@/api/studentLessonPlans'
|
||||
|
||||
const rows = ref([])
|
||||
const loading = ref(false)
|
||||
const pageNo = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const totalCount = ref(0)
|
||||
const searchName = ref('')
|
||||
const tableRef = ref(null)
|
||||
|
||||
async function fetchLessonPlans() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await findStudentLessonPlans(pageNo.value, pageSize.value, searchName.value || '')
|
||||
const d = res.data
|
||||
rows.value = Array.isArray(d.data) ? d.data : []
|
||||
totalCount.value = d.totalCount || 0
|
||||
pageNo.value = d.pageNo || pageNo.value
|
||||
pageSize.value = d.pageSize || pageSize.value
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handlePageChange(p) {
|
||||
pageNo.value = p
|
||||
fetchLessonPlans()
|
||||
}
|
||||
function handleSizeChange(s) {
|
||||
pageSize.value = s
|
||||
pageNo.value = 1
|
||||
fetchLessonPlans()
|
||||
}
|
||||
function onSearch() {
|
||||
pageNo.value = 1
|
||||
fetchLessonPlans()
|
||||
}
|
||||
function onReset() {
|
||||
searchName.value = ''
|
||||
pageNo.value = 1
|
||||
fetchLessonPlans()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchLessonPlans()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user