This commit is contained in:
zlwu committed 2026-01-04 10:00:08 +08:00
1 parent 48f15745be
commit e421030ada
72 files changed
+17064 -35

No files matched your search

@@ -0,0 +1,556 @@
package org.dromara.xg.controller;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.math.BigDecimal;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.xg.domain.vo.XgConductScoreVo;
import org.dromara.xg.domain.bo.XgConductScoreBo;
import org.dromara.xg.domain.bo.XgConductScoreQueryBo;
import org.dromara.xg.domain.bo.XgConductScoreBatchBo;
import org.dromara.xg.domain.vo.XgConductScoreBatchResultVo;
import org.dromara.xg.domain.vo.XgConductScoreClassSummaryVo;
import org.dromara.xg.domain.vo.XgConductScoreSemesterAverageVo;
import org.dromara.xg.service.IXgConductScoreService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
* 学生操行分
*
* @author Lion Li
* @date 2025-01-13
*/
@Validated
@RequiredArgsConstructor
@Slf4j
@RestController
@RequestMapping("/xg/conduct-score")
public class XgConductScoreController extends BaseController {
private final IXgConductScoreService xgConductScoreService;
/**
* 获取班主任负责的班级学生列表(用于录入)
*/
// @SaCheckPermission("xg:conductScore:classStudents")
@GetMapping("/class-students")
public R<XgConductScoreClassSummaryVo> classStudents(
@NotNull(message = "学年不能为空") Integer academicYear,
@NotNull(message = "学期不能为空") Integer semester,
@NotNull(message = "月份不能为空") Integer month) {
return R.ok(xgConductScoreService.getClassStudentsByTeacher(academicYear, semester, month));
}
/**
* 批量录入操行分
*/
// @SaCheckPermission("xg:conductScore:batchAdd")
@RepeatSubmit()
@Log(title = "学生操行分", businessType = BusinessType.INSERT)
@PostMapping("/batch")
public R<XgConductScoreBatchResultVo> batchAdd(@Validated @RequestBody XgConductScoreBatchBo bo) {
return R.ok(xgConductScoreService.batchInsertConductScores(bo));
}
/**
* 查询学生操行分列表
*/
// @SaCheckPermission("xg:conductScore:list")
@GetMapping("/list")
public TableDataInfo<XgConductScoreVo> list(XgConductScoreQueryBo bo, PageQuery pageQuery) {
return xgConductScoreService.queryPageList(bo, pageQuery);
}
/**
* 根据组织ID查询学生操行分列表
*/
// @SaCheckPermission("xg:conductScore:list")
@PostMapping("/listByDeptId")
public TableDataInfo<XgConductScoreVo> listByDeptId(@RequestBody Map<String, Object> requestBody,
PageQuery pageQuery) {
// 从请求体中解析排序参数
if (requestBody.containsKey("orderByColumn")) {
pageQuery.setOrderByColumn((String) requestBody.get("orderByColumn"));
}
if (requestBody.containsKey("isAsc")) {
pageQuery.setIsAsc((String) requestBody.get("isAsc"));
}
// 从请求体中解析查询条件和部门ID
XgConductScoreQueryBo bo = new XgConductScoreQueryBo();
// 设置基础查询条件
if (requestBody.containsKey("academicYear") && requestBody.get("academicYear") != null
&& !"".equals(requestBody.get("academicYear").toString())) {
bo.setAcademicYear(Integer.valueOf(requestBody.get("academicYear").toString()));
}
if (requestBody.containsKey("semester") && requestBody.get("semester") != null
&& !"".equals(requestBody.get("semester").toString())) {
bo.setSemester(Integer.valueOf(requestBody.get("semester").toString()));
}
if (requestBody.containsKey("month") && requestBody.get("month") != null
&& !"".equals(requestBody.get("month").toString())) {
bo.setMonth(Integer.valueOf(requestBody.get("month").toString()));
}
if (requestBody.containsKey("studentName")) {
bo.setStudentName((String) requestBody.get("studentName"));
}
if (requestBody.containsKey("studentNo")) {
bo.setStudentNo((String) requestBody.get("studentNo"));
}
if (requestBody.containsKey("scoreMin")) {
bo.setScoreMin(requestBody.get("scoreMin") != null
? new java.math.BigDecimal(requestBody.get("scoreMin").toString())
: null);
}
if (requestBody.containsKey("scoreMax")) {
bo.setScoreMax(requestBody.get("scoreMax") != null
? new java.math.BigDecimal(requestBody.get("scoreMax").toString())
: null);
}
if (requestBody.containsKey("className")) {
bo.setClassName((String) requestBody.get("className"));
}
if (requestBody.containsKey("collegeName")) {
bo.setCollegeName((String) requestBody.get("collegeName"));
}
if (requestBody.containsKey("majorName")) {
bo.setMajorName((String) requestBody.get("majorName"));
}
// 设置时间范围查询条件
Map<String, Object> params = bo.getParams();
if (requestBody.containsKey("beginTime")) {
params.put("beginTime", requestBody.get("beginTime"));
}
if (requestBody.containsKey("endTime")) {
params.put("endTime", requestBody.get("endTime"));
}
// 提取部门ID参数
Map<String, Object> deptParams = new HashMap<>();
// 支持多种参数名称:deptId(驼峰)和 dept_id(下划线)
Object deptIdValue = null;
if (requestBody.containsKey("deptId") && requestBody.get("deptId") != null) {
deptIdValue = requestBody.get("deptId");
} else if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") != null) {
deptIdValue = requestBody.get("dept_id");
}
// 如果请求体中显式包含 dept_id 且值为 null,则按需求返回空数据
if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") == null) {
log.info("listByDeptId - 请求中 dept_id 为 null,返回空分页数据");
return TableDataInfo.build(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>());
}
if (deptIdValue != null) {
deptParams.put("dept_id", deptIdValue);
}
return xgConductScoreService.queryPageListByDeptId(bo, deptParams, pageQuery);
}
/**
* 导出学生操行分列表
*/
// @SaCheckPermission("xg:conductScore:export")
@Log(title = "学生操行分", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(@RequestParam Map<String, Object> requestBody, HttpServletResponse response) {
// 从请求体中解析查询条件
XgConductScoreQueryBo bo = new XgConductScoreQueryBo();
// 设置基础查询条件
if (requestBody.containsKey("academicYear") && requestBody.get("academicYear") != null
&& !"".equals(requestBody.get("academicYear").toString())) {
bo.setAcademicYear(Integer.valueOf(requestBody.get("academicYear").toString()));
}
if (requestBody.containsKey("semester") && requestBody.get("semester") != null
&& !"".equals(requestBody.get("semester").toString())) {
bo.setSemester(Integer.valueOf(requestBody.get("semester").toString()));
}
if (requestBody.containsKey("month") && requestBody.get("month") != null
&& !"".equals(requestBody.get("month").toString())) {
bo.setMonth(Integer.valueOf(requestBody.get("month").toString()));
}
if (requestBody.containsKey("studentName")) {
bo.setStudentName((String) requestBody.get("studentName"));
}
if (requestBody.containsKey("studentNo")) {
bo.setStudentNo((String) requestBody.get("studentNo"));
}
if (requestBody.containsKey("scoreMin")) {
bo.setScoreMin(requestBody.get("scoreMin") != null
? new java.math.BigDecimal(requestBody.get("scoreMin").toString())
: null);
}
if (requestBody.containsKey("scoreMax")) {
bo.setScoreMax(requestBody.get("scoreMax") != null
? new java.math.BigDecimal(requestBody.get("scoreMax").toString())
: null);
}
if (requestBody.containsKey("className")) {
bo.setClassName((String) requestBody.get("className"));
}
if (requestBody.containsKey("collegeName")) {
bo.setCollegeName((String) requestBody.get("collegeName"));
}
if (requestBody.containsKey("majorName")) {
bo.setMajorName((String) requestBody.get("majorName"));
}
// 设置时间范围查询条件
Map<String, Object> params = bo.getParams();
if (requestBody.containsKey("beginTime")) {
params.put("beginTime", requestBody.get("beginTime"));
}
if (requestBody.containsKey("endTime")) {
params.put("endTime", requestBody.get("endTime"));
}
// 提取部门ID参数
Map<String, Object> deptParams = new HashMap<>();
// 支持多种参数名称:deptId(驼峰)和 dept_id(下划线)
Object deptIdValue = null;
if (requestBody.containsKey("deptId") && requestBody.get("deptId") != null) {
deptIdValue = requestBody.get("deptId");
} else if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") != null) {
deptIdValue = requestBody.get("dept_id");
}
if (deptIdValue != null) {
deptParams.put("dept_id", deptIdValue);
}
// 使用权限过滤的查询方法
List<XgConductScoreVo> list = xgConductScoreService.queryListByDeptId(bo, deptParams);
// 如果需要包含评判结果,进行处理
if (requestBody.containsKey("includeEvaluation") &&
"true".equals(requestBody.get("includeEvaluation").toString())) {
for (XgConductScoreVo vo : list) {
if (vo.getScore() != null) {
vo.setEvaluationResult(calculateEvaluationResult(vo.getScore()));
} else {
vo.setEvaluationResult("");
}
}
}
ExcelUtil.exportExcel(list, "学生操行分", XgConductScoreVo.class, response);
}
/**
* 计算评判结果
*/
private String calculateEvaluationResult(BigDecimal score) {
if (score == null) {
return "";
}
double scoreValue = score.doubleValue();
if (scoreValue >= 85) {
return "优秀";
} else if (scoreValue >= 75) {
return "良好";
} else if (scoreValue >= 60) {
return "合格";
} else {
return "不合格";
}
}
/**
* 查询学期平均分统计
*/
// @SaCheckPermission("xg:conductScore:semesterAverage")
@GetMapping("/semester-average")
public TableDataInfo<XgConductScoreSemesterAverageVo> semesterAverage(
@NotNull(message = "学年不能为空") Integer academicYear,
@NotNull(message = "学期不能为空") Integer semester,
XgConductScoreQueryBo bo, PageQuery pageQuery) {
return xgConductScoreService.getSemesterAverage(academicYear, semester, bo, pageQuery);
}
/**
* 根据部门ID查询学期平均分统计(带权限过滤)
*/
// @SaCheckPermission("xg:conductScore:semesterAverage")
@PostMapping("/semester-average-by-dept-id")
public TableDataInfo<XgConductScoreSemesterAverageVo> semesterAverageByDeptId(
@RequestBody Map<String, Object> requestBody) {
// 从请求体中解析分页参数
PageQuery pageQuery = new PageQuery();
if (requestBody.containsKey("pageNum") && requestBody.get("pageNum") != null) {
pageQuery.setPageNum(Integer.valueOf(requestBody.get("pageNum").toString()));
} else {
pageQuery.setPageNum(1);
}
if (requestBody.containsKey("pageSize") && requestBody.get("pageSize") != null) {
pageQuery.setPageSize(Integer.valueOf(requestBody.get("pageSize").toString()));
} else {
pageQuery.setPageSize(10);
}
// 从请求体中解析查询条件和部门ID
XgConductScoreQueryBo bo = new XgConductScoreQueryBo();
// 设置基础查询条件
if (requestBody.containsKey("academicYear") && requestBody.get("academicYear") != null
&& !"".equals(requestBody.get("academicYear").toString())) {
bo.setAcademicYear(Integer.valueOf(requestBody.get("academicYear").toString()));
}
if (requestBody.containsKey("semester") && requestBody.get("semester") != null
&& !"".equals(requestBody.get("semester").toString())) {
bo.setSemester(Integer.valueOf(requestBody.get("semester").toString()));
}
if (requestBody.containsKey("month") && requestBody.get("month") != null
&& !"".equals(requestBody.get("month").toString())) {
bo.setMonth(Integer.valueOf(requestBody.get("month").toString()));
}
if (requestBody.containsKey("studentName")) {
bo.setStudentName((String) requestBody.get("studentName"));
}
if (requestBody.containsKey("studentNo")) {
bo.setStudentNo((String) requestBody.get("studentNo"));
}
if (requestBody.containsKey("collegeName")) {
bo.setCollegeName((String) requestBody.get("collegeName"));
}
if (requestBody.containsKey("majorName")) {
bo.setMajorName((String) requestBody.get("majorName"));
}
if (requestBody.containsKey("className")) {
bo.setClassName((String) requestBody.get("className"));
}
if (requestBody.containsKey("scoreMin")) {
bo.setScoreMin(requestBody.get("scoreMin") != null
? new java.math.BigDecimal(requestBody.get("scoreMin").toString())
: null);
}
if (requestBody.containsKey("scoreMax")) {
bo.setScoreMax(requestBody.get("scoreMax") != null
? new java.math.BigDecimal(requestBody.get("scoreMax").toString())
: null);
}
// 解析学年和学期(必需参数,用于业务逻辑)
Integer academicYear = null;
Integer semester = null;
if (bo.getAcademicYear() != null) {
academicYear = bo.getAcademicYear();
} else {
throw new IllegalArgumentException("学年不能为空");
}
if (bo.getSemester() != null) {
semester = bo.getSemester();
} else {
throw new IllegalArgumentException("学期不能为空");
}
// 设置时间范围查询条件
Map<String, Object> params = bo.getParams();
if (requestBody.containsKey("beginTime")) {
params.put("beginTime", requestBody.get("beginTime"));
}
if (requestBody.containsKey("endTime")) {
params.put("endTime", requestBody.get("endTime"));
}
// 提取部门ID参数
Map<String, Object> deptParams = new HashMap<>();
// 支持多种参数名称:deptId(驼峰)和 dept_id(下划线)
Object deptIdValue = null;
if (requestBody.containsKey("deptId") && requestBody.get("deptId") != null) {
deptIdValue = requestBody.get("deptId");
} else if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") != null) {
deptIdValue = requestBody.get("dept_id");
}
// 如果请求体中显式包含 dept_id 且值为 null,则按需求返回空数据
if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") == null) {
log.info("semesterAverageByDeptId - 请求中 dept_id 为 null,返回空分页数据");
return TableDataInfo.build(new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>());
}
if (deptIdValue != null) {
deptParams.put("dept_id", deptIdValue);
}
return xgConductScoreService.getSemesterAverageByDeptId(academicYear, semester, bo, deptParams, pageQuery);
}
/**
* 导出学期平均分统计
*/
// @SaCheckPermission("xg:conductScore:exportSemester")
@Log(title = "学期平均分统计", businessType = BusinessType.EXPORT)
@PostMapping("/export-semester")
public void exportSemester(@RequestParam Map<String, Object> requestBody, HttpServletResponse response) {
// 从请求体中解析查询条件
XgConductScoreQueryBo bo = new XgConductScoreQueryBo();
// 设置基础查询条件
if (requestBody.containsKey("academicYear") && requestBody.get("academicYear") != null
&& !"".equals(requestBody.get("academicYear").toString())) {
bo.setAcademicYear(Integer.valueOf(requestBody.get("academicYear").toString()));
}
if (requestBody.containsKey("semester") && requestBody.get("semester") != null
&& !"".equals(requestBody.get("semester").toString())) {
bo.setSemester(Integer.valueOf(requestBody.get("semester").toString()));
}
if (requestBody.containsKey("month") && requestBody.get("month") != null
&& !"".equals(requestBody.get("month").toString())) {
bo.setMonth(Integer.valueOf(requestBody.get("month").toString()));
}
if (requestBody.containsKey("studentName")) {
bo.setStudentName((String) requestBody.get("studentName"));
}
if (requestBody.containsKey("studentNo")) {
bo.setStudentNo((String) requestBody.get("studentNo"));
}
if (requestBody.containsKey("collegeName")) {
bo.setCollegeName((String) requestBody.get("collegeName"));
}
if (requestBody.containsKey("majorName")) {
bo.setMajorName((String) requestBody.get("majorName"));
}
if (requestBody.containsKey("className")) {
bo.setClassName((String) requestBody.get("className"));
}
if (requestBody.containsKey("scoreMin")) {
bo.setScoreMin(requestBody.get("scoreMin") != null
? new java.math.BigDecimal(requestBody.get("scoreMin").toString())
: null);
}
if (requestBody.containsKey("scoreMax")) {
bo.setScoreMax(requestBody.get("scoreMax") != null
? new java.math.BigDecimal(requestBody.get("scoreMax").toString())
: null);
}
// 解析学年和学期(必需参数,用于业务逻辑)
Integer academicYear = null;
Integer semester = null;
if (bo.getAcademicYear() != null) {
academicYear = bo.getAcademicYear();
} else {
throw new IllegalArgumentException("学年不能为空");
}
if (bo.getSemester() != null) {
semester = bo.getSemester();
} else {
throw new IllegalArgumentException("学期不能为空");
}
// 设置时间范围查询条件
Map<String, Object> params = bo.getParams();
if (requestBody.containsKey("beginTime")) {
params.put("beginTime", requestBody.get("beginTime"));
}
if (requestBody.containsKey("endTime")) {
params.put("endTime", requestBody.get("endTime"));
}
// 提取部门ID参数
Map<String, Object> deptParams = new HashMap<>();
// 支持多种参数名称:deptId(驼峰)和 dept_id(下划线)
Object deptIdValue = null;
if (requestBody.containsKey("deptId") && requestBody.get("deptId") != null) {
deptIdValue = requestBody.get("deptId");
} else if (requestBody.containsKey("dept_id") && requestBody.get("dept_id") != null) {
deptIdValue = requestBody.get("dept_id");
}
if (deptIdValue != null) {
deptParams.put("dept_id", deptIdValue);
}
// 使用权限过滤的查询方法获取所有数据(设置大分页以获取全部数据)
PageQuery pageQuery = new PageQuery();
pageQuery.setPageNum(1);
pageQuery.setPageSize(999999); // 设置足够大的页面大小以获取所有数据
List<XgConductScoreSemesterAverageVo> list = xgConductScoreService
.getSemesterAverageByDeptId(academicYear, semester, bo, deptParams, pageQuery).getRows();
// 为每条数据计算评判结果
for (XgConductScoreSemesterAverageVo vo : list) {
if (vo.getAvgScore() != null && vo.getRecordCount() != null && vo.getRecordCount() > 0) {
vo.setEvaluationResult(calculateEvaluationResult(vo.getAvgScore()));
} else {
vo.setEvaluationResult("");
}
}
ExcelUtil.exportExcel(list, "学期平均分统计", XgConductScoreSemesterAverageVo.class, response);
}
/**
* 获取单个学生操行分记录详情
*/
// @SaCheckPermission("xg:conductScore:studentRecords")
@GetMapping("/student-records/{studentId}")
public R<org.dromara.xg.domain.vo.XgConductScoreStudentRecordsVo> getStudentRecords(
@NotNull(message = "学生ID不能为空") @PathVariable Long studentId,
Integer academicYear, Integer semester, String tenantId) {
return R.ok(xgConductScoreService.getStudentRecords(studentId, academicYear, semester, tenantId));
}
/**
* 获取班级详细统计数据
*/
// @SaCheckPermission("xg:conductScore:classDetail")
@GetMapping("/class-detail/{classId}")
public R<org.dromara.xg.domain.vo.XgConductScoreClassDetailVo> getClassDetail(
@NotNull(message = "班级ID不能为空") @PathVariable Long classId,
@NotNull(message = "学年不能为空") Integer academicYear,
@NotNull(message = "学期不能为空") Integer semester) {
return R.ok(xgConductScoreService.getClassDetailStatistics(classId, academicYear, semester));
}
/**
* 修改单个操行分记录
*/
// @SaCheckPermission("xg:conductScore:update")
@RepeatSubmit()
@Log(title = "学生操行分", businessType = BusinessType.UPDATE)
@PutMapping("/{id}")
public R<Void> updateRecord(@NotNull(message = "记录ID不能为空") @PathVariable Long id,
@Validated @RequestBody XgConductScoreBo bo) {
return toAjax(xgConductScoreService.updateRecordById(id, bo));
}
/**
* 删除学生操行分
*/
// @SaCheckPermission("xg:conductScore:remove")
@Log(title = "学生操行分", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
return toAjax(xgConductScoreService.deleteWithValidByIds(List.of(ids), true));
}
/**
* 验证录入权限
*/
// @SaCheckPermission("xg:conductScore:checkPermission")
@GetMapping("/check-permission")
public R<Boolean> checkPermission() {
return R.ok(xgConductScoreService.checkInputPermission());
}
}
@@ -0,0 +1,53 @@
package org.dromara.xg.domain;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
/**
* 学生操行分对象 xg_conduct_score
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class XgConductScore extends BaseEntity {
private static final long serialVersionUID = 1L;
/** ID */
private Long id;
/** 租户ID */
private String tenantId;
/** 学年 */
private Integer academicYear;
/** 学期 */
private Integer semester;
/** 月份 */
private Integer month;
/** 学生ID */
private Long studentId;
/** 班级ID */
private Long classId;
/** 班主任ID */
private Long classTeacherId;
/** 分数 */
private BigDecimal score;
/** 备注 */
private String remark;
/** 删除标志 */
private Integer deleted;
}
@@ -0,0 +1,43 @@
package org.dromara.xg.domain.bo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 学生操行分批量录入业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreBatchBo {
private static final long serialVersionUID = 1L;
/**
* 学年
*/
private Integer academicYear;
/**
* 学期
*/
private Integer semester;
/**
* 月份
*/
private Integer month;
/**
* 班级ID(前端传递字符串避免精度问题)
*/
private String classId;
/**
* 录入分数列表
*/
private List<XgConductScoreBatchItemBo> scores;
}
@@ -0,0 +1,37 @@
package org.dromara.xg.domain.bo;
import lombok.Data;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
/**
* 学生操行分批量录入项业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreBatchItemBo {
private static final long serialVersionUID = 1L;
/**
* 学生ID
*/
@NotNull(message = "学生ID不能为空")
private Long studentId;
/**
* 分数
*/
@NotNull(message = "分数不能为空")
@DecimalMin(value = "0", message = "分数不能小于0")
@DecimalMax(value = "100", message = "分数不能大于100")
private BigDecimal score;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,82 @@
package org.dromara.xg.domain.bo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
/**
* 学生操行分业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class XgConductScoreBo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* ID
*/
@NotNull(message = "ID不能为空", groups = { EditGroup.class })
private Long id;
/**
* 租户ID
*/
private String tenantId;
/**
* 学年
*/
@NotNull(message = "学年不能为空", groups = { AddGroup.class, EditGroup.class })
private Integer academicYear;
/**
* 学期
*/
@NotNull(message = "学期不能为空", groups = { AddGroup.class, EditGroup.class })
private Integer semester;
/**
* 月份
*/
@NotNull(message = "月份不能为空", groups = { AddGroup.class, EditGroup.class })
private Integer month;
/**
* 学生ID
*/
@NotNull(message = "学生ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long studentId;
/**
* 班级ID
*/
@NotNull(message = "班级ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long classId;
/**
* 班主任ID
*/
private Long classTeacherId;
/**
* 分数
*/
@NotNull(message = "分数不能为空", groups = { AddGroup.class, EditGroup.class })
@DecimalMin(value = "0", message = "分数不能小于0", groups = { AddGroup.class, EditGroup.class })
@DecimalMax(value = "100", message = "分数不能大于100", groups = { AddGroup.class, EditGroup.class })
private BigDecimal score;
/**
* 备注
*/
private String remark;
}
@@ -0,0 +1,105 @@
package org.dromara.xg.domain.bo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.math.BigDecimal;
/**
* 学生操行分查询业务对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class XgConductScoreQueryBo extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 租户ID
*/
private String tenantId;
/**
* 学年
*/
private Integer academicYear;
/**
* 学期
*/
private Integer semester;
/**
* 月份
*/
private Integer month;
/**
* 学生ID
*/
private Long studentId;
/**
* 学生姓名
*/
private String studentName;
/**
* 学号
*/
private String studentNo;
/**
* 班级ID
*/
private Long classId;
/**
* 班级名称
*/
private String className;
/**
* 学院名称
*/
private String collegeName;
/**
* 专业名称
*/
private String majorName;
/**
* 班主任ID
*/
private Long classTeacherId;
/**
* 最低分数
*/
private BigDecimal scoreMin;
/**
* 最高分数
*/
private BigDecimal scoreMax;
/**
* 备注
*/
private String remark;
/**
* 开始时间
*/
private String beginTime;
/**
* 结束时间
*/
private String endTime;
}
@@ -0,0 +1,32 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.util.List;
/**
* 操行分批量录入结果视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreBatchResultVo {
private static final long serialVersionUID = 1L;
/**
* 成功录入数量
*/
private Integer successCount;
/**
* 失败录入数量
*/
private Integer failCount;
/**
* 失败详情列表
*/
private List<String> failList;
}
@@ -0,0 +1,73 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 班级操行分详细统计视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreClassDetailVo {
private static final long serialVersionUID = 1L;
/**
* 班级ID
*/
private Long classId;
/**
* 班级名称
*/
private String className;
/**
* 学年
*/
private Integer academicYear;
/**
* 学期
*/
private Integer semester;
/**
* 总学生数
*/
private Integer totalStudents;
/**
* 已录入学生数
*/
private Integer recordedStudents;
/**
* 录入率
*/
private BigDecimal recordRate;
/**
* 班级平均分
*/
private BigDecimal classAverage;
/**
* 最高分
*/
private BigDecimal maxScore;
/**
* 最低分
*/
private BigDecimal minScore;
/**
* 学生详细列表
*/
private List<XgConductScoreClassStudentDetailVo> students;
}
@@ -0,0 +1,47 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* 班级学生操行分详细视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreClassStudentDetailVo {
private static final long serialVersionUID = 1L;
/**
* 学生ID
*/
private Long studentId;
/**
* 学号
*/
private String studentNo;
/**
* 学生姓名
*/
private String studentName;
/**
* 性别
*/
private String gender;
/**
* 平均分
*/
private BigDecimal avgScore;
/**
* 录入次数
*/
private Integer recordCount;
}
@@ -0,0 +1,73 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 班级学生视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreClassStudentVo {
private static final long serialVersionUID = 1L;
/**
* 学生ID
*/
private Long studentId;
/**
* 学号
*/
private String studentNo;
/**
* 学生姓名
*/
private String studentName;
/**
* 性别
*/
private String gender;
/**
* 上月分数
*/
private BigDecimal lastScore;
/**
* 上月月份
*/
private Integer lastMonth;
/**
* 现有分数
*/
private XgConductScoreDetailVo existingScore;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,27 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.util.List;
/**
* 操行分班级汇总视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreClassSummaryVo {
private static final long serialVersionUID = 1L;
/**
* 班级列表
*/
private List<XgConductScoreClassVo> classes;
/**
* 汇总统计
*/
private XgConductScoreSummaryVo summary;
}
@@ -0,0 +1,78 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* 班主任班级视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreClassVo {
private static final long serialVersionUID = 1L;
/**
* 班级ID
*/
private Long classId;
/**
* 班级名称
*/
private String className;
/**
* 班级代码
*/
private String classCode;
/**
* 学生人数
*/
private Integer studentCount;
/**
* 入学年份
*/
private Integer enrollmentYear;
/**
* 年级
*/
private String grade;
/**
* 专业名称
*/
private String majorName;
/**
* 学生列表
*/
private List<XgConductScoreClassStudentVo> students;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,53 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 操行分详情视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreDetailVo {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 分数
*/
private BigDecimal score;
/**
* 备注
*/
private String remark;
/**
* 创建时间
*/
private Date createTime;
/**
* 创建者
*/
private Long createBy;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,78 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* 操行分记录详情视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreRecordDetailVo {
private static final long serialVersionUID = 1L;
/**
* ID
*/
private Long id;
/**
* 学年
*/
private Integer academicYear;
/**
* 学期
*/
private Integer semester;
/**
* 月份
*/
private Integer month;
/**
* 分数
*/
private BigDecimal score;
/**
* 备注
*/
private String remark;
/**
* 创建者
*/
private Long createBy;
/**
* 创建者姓名
*/
private String createByName;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新者姓名
*/
private String updateByName;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,142 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
/**
* 操行分学期平均分视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@ExcelIgnoreUnannotated
public class XgConductScoreSemesterAverageVo {
private static final long serialVersionUID = 1L;
/**
* 学生ID
*/
private Long studentId;
/**
* 学号
*/
@ExcelProperty(value = "学号")
private String studentNo;
/**
* 学生姓名
*/
@ExcelProperty(value = "学生姓名")
private String studentName;
/**
* 性别
*/
@ExcelProperty(value = "性别", converter = org.dromara.common.excel.convert.ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "0=男,1=女")
private String gender;
/**
* 班级ID
*/
private Long classId;
/**
* 学院名称
*/
@ExcelProperty(value = "学院")
private String collegeName;
/**
* 专业名称
*/
@ExcelProperty(value = "专业")
private String majorName;
/**
* 班级名称
*/
@ExcelProperty(value = "班级")
private String className;
/**
* 学年
*/
private Integer academicYear;
/**
* 学期
*/
@ExcelProperty(value = "学期", converter = org.dromara.common.excel.convert.ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "1=第一学期,2=第二学期")
private Integer semester;
/**
* 学期平均分
*/
@ExcelProperty(value = "学期平均分")
private BigDecimal avgScore;
/**
* 评判结果
*/
@ExcelProperty(value = "评判结果")
private String evaluationResult;
/**
* 录入月份数
*/
@ExcelProperty(value = "录入月份")
private Integer recordCount;
/**
* 最高分
*/
@ExcelProperty(value = "最高分")
private BigDecimal maxScore;
/**
* 最低分
*/
@ExcelProperty(value = "最低分")
private BigDecimal minScore;
/**
* 分数范围
*/
private String scoreRange;
/**
* 最后更新时间
*/
private Date lastUpdateTime;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
}
@@ -0,0 +1,73 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 学生操行分记录详情视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreStudentRecordsVo {
private static final long serialVersionUID = 1L;
/**
* 学生ID
*/
private Long studentId;
/**
* 学号
*/
private String studentNo;
/**
* 学生姓名
*/
private String studentName;
/**
* 性别
*/
private String gender;
/**
* 班级ID
*/
private Long classId;
/**
* 班级名称
*/
private String className;
/**
* 班主任ID
*/
private Long classTeacherId;
/**
* 班主任姓名
*/
private String classTeacherName;
/**
* 学期平均分
*/
private BigDecimal semesterAverage;
/**
* 总记录数
*/
private Integer totalRecords;
/**
* 详细记录列表
*/
private List<XgConductScoreRecordDetailVo> records;
}
@@ -0,0 +1,42 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
/**
* 操行分汇总统计视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
public class XgConductScoreSummaryVo {
private static final long serialVersionUID = 1L;
/**
* 总班级数
*/
private Integer totalClasses;
/**
* 总学生数
*/
private Integer totalStudents;
/**
* 已录入学生数
*/
private Integer recordedStudents;
/**
* 待录入学生数
*/
private Integer pendingStudents;
/**
* 完成度
*/
private BigDecimal completionRate;
}
@@ -0,0 +1,155 @@
package org.dromara.xg.domain.vo;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.io.Serial;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonFormat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import org.dromara.xg.domain.XgConductScore;
import io.github.linpeilie.annotations.AutoMapper;
/**
* 学生操行分视图对象
*
* @author Lion Li
* @date 2025-01-13
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = XgConductScore.class)
public class XgConductScoreVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@cn.idev.excel.annotation.ExcelProperty(value = "记录ID")
private Long id;
/**
* 租户ID
*/
private String tenantId;
/**
* 学年
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学年")
private Integer academicYear;
/**
* 学期
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学期", converter = org.dromara.common.excel.convert.ExcelDictConvert.class)
@org.dromara.common.excel.annotation.ExcelDictFormat(readConverterExp = "1=第一学期,2=第二学期")
private Integer semester;
/**
* 月份
*/
@cn.idev.excel.annotation.ExcelProperty(value = "月份", converter = org.dromara.common.excel.convert.ExcelDictConvert.class)
@org.dromara.common.excel.annotation.ExcelDictFormat(readConverterExp = "1=1月,2=2月,3=3月,4=4月,5=5月,6=6月,7=7月,8=8月,9=9月,10=10月,11=11月,12=12月")
private Integer month;
/**
* 学生ID
*/
private Long studentId;
/**
* 学生姓名
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学生姓名")
private String studentName;
/**
* 学号
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学号")
private String studentNo;
/**
* 班级ID
*/
private Long classId;
/**
* 班级名称
*/
@cn.idev.excel.annotation.ExcelProperty(value = "班级名称")
private String className;
/**
* 学院名称
*/
@cn.idev.excel.annotation.ExcelProperty(value = "学院名称")
private String collegeName;
/**
* 专业名称
*/
@cn.idev.excel.annotation.ExcelProperty(value = "专业名称")
private String majorName;
/**
* 班主任ID
*/
private Long classTeacherId;
/**
* 班主任姓名
*/
private String classTeacherName;
/**
* 分数
*/
@cn.idev.excel.annotation.ExcelProperty(value = "分数")
private BigDecimal score;
/**
* 备注
*/
@cn.idev.excel.annotation.ExcelProperty(value = "备注")
private String remark;
/**
* 评判结果
*/
@cn.idev.excel.annotation.ExcelProperty(value = "评判结果")
private String evaluationResult;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@cn.idev.excel.annotation.ExcelProperty(value = "录入时间")
private Date createTime;
/**
* 创建者
*/
private Long createBy;
/**
* 创建者姓名
*/
@cn.idev.excel.annotation.ExcelProperty(value = "录入人")
private String createByName;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
}
@@ -0,0 +1,149 @@
package org.dromara.xg.mapper;
import org.dromara.xg.domain.XgConductScore;
import org.dromara.xg.domain.bo.XgConductScoreQueryBo;
import org.dromara.xg.domain.vo.XgConductScoreVo;
import org.dromara.xg.domain.vo.XgConductScoreSemesterAverageVo;
import org.dromara.xg.domain.vo.XgConductScoreStudentRecordsVo;
import org.dromara.xg.domain.vo.XgConductScoreClassDetailVo;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import java.util.List;
import java.util.Map;
/**
* 学生操行分Mapper接口
*
* @author Lion Li
* @date 2025-01-13
*/
public interface IXgConductScoreMapper extends BaseMapperPlus<XgConductScore, XgConductScoreVo> {
/**
* 查询学生操行分列表(包含关联数据)
*
* @param queryBo 查询条件
* @return 学生操行分列表
*/
List<XgConductScoreVo> selectXgConductScoreList(XgConductScoreQueryBo queryBo);
/**
* 根据部门ID列表分页查询学生操行分列表
*
* @param pageQuery 分页参数
* @param bo 查询条件BO
* @param classIdList 班级ID列表
* @return 分页结果
*/
com.baomidou.mybatisplus.core.metadata.IPage<XgConductScoreVo> selectPageByDeptIds(
@Param("page") com.baomidou.mybatisplus.core.metadata.IPage<XgConductScoreVo> pageQuery,
@Param("bo") org.dromara.xg.domain.bo.XgConductScoreQueryBo bo,
@Param("classIdList") java.util.List<String> classIdList);
/**
* 根据部门ID查询学生操行分列表(用于导出)
*
* @param bo 查询条件
* @param classIdList 班级ID列表
* @return 学生操行分列表
*/
java.util.List<XgConductScoreVo> selectListByDeptIds(
@Param("bo") org.dromara.xg.domain.bo.XgConductScoreQueryBo bo,
@Param("classIdList") java.util.List<String> classIdList);
/**
* 查询班主任负责的班级学生列表
*
* @param teacherId 班主任ID
* @param academicYear 学年
* @param semester 学期
* @param month 月份
* @return 学生列表
*/
List<Map<String, Object>> selectClassStudentsByTeacher(Long teacherId, Integer academicYear, Integer semester,
Integer month);
/**
* 查询学期平均分统计列表
*
* @param queryBo 查询条件
* @return 统计列表
*/
List<XgConductScoreSemesterAverageVo> selectSemesterAverageList(Object queryBo);
/**
* 根据班级ID列表查询学期平均分统计(支持权限过滤,显示所有学生)
*
* @param queryBo 查询条件
* @param classIdList 班级ID列表
* @return 学期平均分统计列表
*/
List<XgConductScoreSemesterAverageVo> selectSemesterAverageListByDeptIds(@Param("queryBo") Object queryBo, @Param("classIdList") List<String> classIdList);
/**
* 检查班主任班级权限
*
* @param teacherId 班主任ID
* @param classId 班级ID
* @return 是否有权限
*/
boolean checkTeacherClass(Long teacherId, Long classId);
/**
* 检查学生当月是否已存在记录
*
* @param studentId 学生ID
* @param academicYear 学年
* @param semester 学期
* @param month 月份
* @param tenantId 租户ID
* @return 是否存在
*/
boolean existsByStudentAndMonth(@org.apache.ibatis.annotations.Param("studentId") Long studentId,
@org.apache.ibatis.annotations.Param("academicYear") Integer academicYear,
@org.apache.ibatis.annotations.Param("semester") Integer semester,
@org.apache.ibatis.annotations.Param("month") Integer month,
@org.apache.ibatis.annotations.Param("tenantId") String tenantId);
/**
* 批量插入操行分记录
*
* @param conductScoreList 记录列表
* @return 影响行数
*/
int insertBatch(List<XgConductScore> conductScoreList);
/**
* 查询班主任负责的班级ID列表
*
* @param teacherId 班主任ID
* @return 班级ID列表
*/
List<Long> selectTeacherClassIds(Long teacherId);
/**
* 查询单个学生的详细记录
*
* @param studentId 学生ID
* @param academicYear 学年(可选)
* @param semester 学期(可选)
* @param tenantId 租户ID
* @return 学生详细记录列表(包含学生信息和所有记录)
*/
java.util.List<java.util.Map<String, Object>> selectStudentRecordsRaw(Long studentId, Integer academicYear, Integer semester, String tenantId);
/**
* 获取班级详细统计数据
*
* @param classId 班级ID
* @param academicYear 学年
* @param semester 学期
* @return 班级详细统计
*/
XgConductScoreClassDetailVo selectClassDetailStatistics(Long classId, Integer academicYear, Integer semester);
}
@@ -0,0 +1,214 @@
package org.dromara.xg.service;
import org.dromara.xg.domain.vo.XgConductScoreVo;
import org.dromara.xg.domain.bo.XgConductScoreBo;
import org.dromara.xg.domain.bo.XgConductScoreQueryBo;
import org.dromara.xg.domain.bo.XgConductScoreBatchBo;
import org.dromara.xg.domain.vo.XgConductScoreBatchResultVo;
import org.dromara.xg.domain.vo.XgConductScoreClassSummaryVo;
import org.dromara.xg.domain.vo.XgConductScoreSemesterAverageVo;
import org.dromara.xg.domain.vo.XgConductScoreStudentRecordsVo;
import org.dromara.xg.domain.vo.XgConductScoreClassDetailVo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* 学生操行分Service接口
*
* @author Lion Li
* @date 2025-01-13
*/
public interface IXgConductScoreService {
/**
* 查询学生操行分
*
* @param id 主键
* @return 学生操行分
*/
XgConductScoreVo queryById(Long id);
/**
* 分页查询学生操行分列表
*
* @param bo 查询条件
* @param pageQuery 分页参数
* @return 学生操行分页列表
*/
TableDataInfo<XgConductScoreVo> queryPageList(XgConductScoreQueryBo bo, PageQuery pageQuery);
/**
* 根据组织ID分页查询学生操行分列表(带权限过滤)
*
* @param bo 查询条件
* @param params 包含dept_id的参数Map
* @param pageQuery 分页参数
* @return 学生操行分页列表
*/
TableDataInfo<XgConductScoreVo> queryPageListByDeptId(XgConductScoreQueryBo bo,
java.util.Map<String, Object> params, PageQuery pageQuery);
/**
* 查询符合条件的学生操行分列表
*
* @param bo 查询条件
* @return 学生操行分列表
*/
List<XgConductScoreVo> queryList(XgConductScoreQueryBo bo);
/**
* 根据部门ID查询学生操行分列表(支持权限过滤)
*
* @param bo 查询条件
* @param params 部门参数
* @return 学生操行分列表
*/
List<XgConductScoreVo> queryListByDeptId(XgConductScoreQueryBo bo, java.util.Map<String, Object> params);
/**
* 新增学生操行分
*
* @param bo 学生操行分
* @return 结果
*/
Boolean insertByBo(XgConductScoreBo bo);
/**
* 修改学生操行分
*
* @param bo 学生操行分
* @return 结果
*/
Boolean updateByBo(XgConductScoreBo bo);
/**
* 校验并批量删除学生操行分信息
*
* @param ids 待删除的主键集合
* @param isValid 是否校验,true-删除前校验,false-直接删除
* @return 结果
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 批量录入操行分
*
* @param bo 批量录入参数
* @return 录入结果
*/
XgConductScoreBatchResultVo batchInsertConductScores(XgConductScoreBatchBo bo);
/**
* 获取班主任负责的班级学生列表
*
* @param academicYear 学年
* @param semester 学期
* @param month 月份
* @return 班级学生汇总信息
*/
XgConductScoreClassSummaryVo getClassStudentsByTeacher(Integer academicYear, Integer semester, Integer month);
/**
* 查询学期平均分统计
*
* @param academicYear 学年
* @param semester 学期
* @param queryBo 查询条件
* @param pageQuery 分页参数
* @return 学期平均分分页列表
*/
TableDataInfo<XgConductScoreSemesterAverageVo> getSemesterAverage(Integer academicYear, Integer semester,
XgConductScoreQueryBo queryBo, PageQuery pageQuery);
/**
* 根据部门ID查询学期平均分统计(支持权限过滤)
*
* @param academicYear 学年
* @param semester 学期
* @param queryBo 查询条件
* @param params 包含dept_id的参数Map
* @param pageQuery 分页参数
* @return 学期平均分分页列表
*/
TableDataInfo<XgConductScoreSemesterAverageVo> getSemesterAverageByDeptId(Integer academicYear, Integer semester,
XgConductScoreQueryBo queryBo, java.util.Map<String, Object> params, PageQuery pageQuery);
/**
* 检查班主任权限
*
* @param classId 班级ID
* @param teacherId 班主任ID
* @return 是否有权限
*/
boolean checkTeacherPermission(Long classId, Long teacherId);
/**
* 检查学生当月是否已存在记录
*
* @param studentId 学生ID
* @param academicYear 学年
* @param semester 学期
* @param month 月份
* @param tenantId 租户ID
* @return 是否存在
*/
boolean existsByStudentAndMonth(Long studentId, Integer academicYear, Integer semester, Integer month,
String tenantId);
/**
* 获取班主任负责的班级ID列表
*
* @param teacherId 班主任ID
* @return 班级ID列表
*/
List<Long> getTeacherClassIds(Long teacherId);
/**
* 查询单个学生的详细记录
*
* @param studentId 学生ID
* @param academicYear 学年(可选)
* @param semester 学期(可选)
* @return 学生详细记录
*/
/**
* 获取学生详细记录
*
* @param studentId 学生ID
* @param academicYear 学年(可选)
* @param semester 学期(可选)
* @param tenantId 租户ID
* @return 学生详细记录
*/
XgConductScoreStudentRecordsVo getStudentRecords(Long studentId, Integer academicYear, Integer semester, String tenantId);
/**
* 获取班级详细统计数据
*
* @param classId 班级ID
* @param academicYear 学年
* @param semester 学期
* @return 班级详细统计
*/
XgConductScoreClassDetailVo getClassDetailStatistics(Long classId, Integer academicYear, Integer semester);
/**
* 修改单个操行分记录
*
* @param id 记录ID
* @param bo 修改参数
* @return 结果
*/
Boolean updateRecordById(Long id, XgConductScoreBo bo);
/**
* 验证录入权限
*
* @return 是否有录入权限
*/
Boolean checkInputPermission();
}
@@ -21,11 +21,16 @@ import org.dromara.xg.mapper.XgClassTeacherCounselorRelMapper;
import org.dromara.xg.mapper.XgStudentMsgMapper;
import org.dromara.xg.mapper.XgClassMsgMapper;
import org.dromara.xg.service.IXgClassTeacherCounselorRelService;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.mapper.SysUserRoleMapper;
import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.Objects;
/**
* 班级班主任辅导员关联Service业务层处理
@@ -41,11 +46,14 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
private final XgClassTeacherCounselorRelMapper baseMapper;
private final XgClassMsgMapper classMsgMapper;
private final XgStudentMsgMapper xgStudentMsgMapper;
private final SysUserRoleMapper sysUserRoleMapper;
// 班主任类型常量
private static final String HEAD_TEACHER_TYPE = "class_teacher";
// 辅导员类型常量
private static final String COUNSELOR_TYPE = "counselor";
// 班主任角色ID常量
private static final Long HEAD_TEACHER_ROLE_ID = 1947639680473493505L;
/**
* 查询班级班主任辅导员关联
@@ -127,6 +135,12 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
List<XgClassTeacherCounselorRel> currentRecords = baseMapper.selectList(queryWrapper);
log.info("班级ID[{}]的班主任查询结果: 找到{}条记录", bo.getClassId(), currentRecords.size());
// 🔴 新增:收集要被替换的旧班主任ID,用于后续角色清理
Set<Long> oldTeacherIds = currentRecords.stream()
.map(XgClassTeacherCounselorRel::getTeacherId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
// 2. 将当前班级的班主任记录标记为删除并设置结束日期
if (!currentRecords.isEmpty()) {
for (XgClassTeacherCounselorRel record : currentRecords) {
@@ -149,6 +163,31 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
bo.getTeacherName() == null ? -1 : bo.getTeacherName().length(),
bo.getTeacherName() == null ? "null" : bo.getTeacherName().trim());
// 🔴 新增:处理新班主任角色分配
if (bo.getTeacherId() != null && bo.getTeacherName() != null
&& !"none123".equals(bo.getTeacherName().trim())) {
// 检查新班主任是否已有班主任角色
LambdaQueryWrapper<SysUserRole> roleQuery = Wrappers.lambdaQuery();
roleQuery.eq(SysUserRole::getUserId, bo.getTeacherId())
.eq(SysUserRole::getRoleId, HEAD_TEACHER_ROLE_ID);
if (sysUserRoleMapper.selectCount(roleQuery) == 0) {
// 不存在则新增角色
SysUserRole newRole = new SysUserRole();
newRole.setUserId(bo.getTeacherId());
newRole.setRoleId(HEAD_TEACHER_ROLE_ID);
try {
sysUserRoleMapper.insert(newRole);
log.info("为新班主任用户ID[{}]分配班主任角色成功", bo.getTeacherId());
} catch (Exception e) {
log.error("为新班主任用户ID[{}]分配班主任角色失败: {}", bo.getTeacherId(), e.getMessage(), e);
// 不抛出异常,避免影响主业务流程
}
} else {
log.info("新班主任用户ID[{}]已拥有班主任角色,跳过分配", bo.getTeacherId());
}
}
if (bo.getTeacherName() == null || "none123".equals(bo.getTeacherName())
|| (bo.getTeacherName() != null
&& ("none123".equals(bo.getTeacherName().trim())
@@ -177,6 +216,36 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
int updateResult11 = xgStudentMsgMapper.update(null, studentUpdateWrapper);
log.info("更新学生表中的班主任信息: classId=[{}], teacherId=[{}], teacherName=[{}], 结果=[{}]",
bo.getClassId(), bo.getTeacherId(), bo.getTeacherName(), updateResult11 > 0 ? "成功" : "失败");
// 🔴 新增:清理旧班主任角色
for (Long oldTeacherId : oldTeacherIds) {
// 检查该教师是否还在其他班担任班主任
LambdaQueryWrapper<XgClassTeacherCounselorRel> checkQuery = Wrappers.lambdaQuery();
checkQuery.eq(XgClassTeacherCounselorRel::getTeacherId, oldTeacherId)
.eq(XgClassTeacherCounselorRel::getTeacherType, HEAD_TEACHER_TYPE)
.eq(XgClassTeacherCounselorRel::getDeleted, 0)
.ne(XgClassTeacherCounselorRel::getClassId, bo.getClassId()); // 排除当前班级
if (baseMapper.selectCount(checkQuery) == 0) {
// 该教师已不在其他班担任班主任,删除角色
LambdaQueryWrapper<SysUserRole> deleteRoleQuery = Wrappers.lambdaQuery();
deleteRoleQuery.eq(SysUserRole::getUserId, oldTeacherId)
.eq(SysUserRole::getRoleId, HEAD_TEACHER_ROLE_ID);
try {
int deleteCount = sysUserRoleMapper.delete(deleteRoleQuery);
if (deleteCount > 0) {
log.info("旧班主任用户ID[{}]已不再担任任何班级班主任,删除班主任角色", oldTeacherId);
}
} catch (Exception e) {
log.error("删除旧班主任用户ID[{}]的班主任角色失败: {}", oldTeacherId, e.getMessage(), e);
// 不抛出异常,避免影响主业务流程
}
} else {
log.info("旧班主任用户ID[{}]仍在其他班担任班主任,保留角色", oldTeacherId);
}
}
// 班主任信息为空,不插入新数据,直接返回
return true;
} else {
@@ -202,6 +271,35 @@ public class XgClassTeacherCounselorRelServiceImpl implements IXgClassTeacherCou
log.info("更新学生表中的班主任信息: classId=[{}], teacherId=[{}], teacherName=[{}], 结果=[{}]",
bo.getClassId(), bo.getTeacherId(), bo.getTeacherName(), updateResult11 > 0 ? "成功" : "失败");
// 🔴 新增:清理旧班主任角色
for (Long oldTeacherId : oldTeacherIds) {
// 检查该教师是否还在其他班担任班主任
LambdaQueryWrapper<XgClassTeacherCounselorRel> checkQuery = Wrappers.lambdaQuery();
checkQuery.eq(XgClassTeacherCounselorRel::getTeacherId, oldTeacherId)
.eq(XgClassTeacherCounselorRel::getTeacherType, HEAD_TEACHER_TYPE)
.eq(XgClassTeacherCounselorRel::getDeleted, 0)
.ne(XgClassTeacherCounselorRel::getClassId, bo.getClassId()); // 排除当前班级
if (baseMapper.selectCount(checkQuery) == 0) {
// 该教师已不在其他班担任班主任,删除角色
LambdaQueryWrapper<SysUserRole> deleteRoleQuery = Wrappers.lambdaQuery();
deleteRoleQuery.eq(SysUserRole::getUserId, oldTeacherId)
.eq(SysUserRole::getRoleId, HEAD_TEACHER_ROLE_ID);
try {
int deleteCount = sysUserRoleMapper.delete(deleteRoleQuery);
if (deleteCount > 0) {
log.info("旧班主任用户ID[{}]已不再担任任何班级班主任,删除班主任角色", oldTeacherId);
}
} catch (Exception e) {
log.error("删除旧班主任用户ID[{}]的班主任角色失败: {}", oldTeacherId, e.getMessage(), e);
// 不抛出异常,避免影响主业务流程
}
} else {
log.info("旧班主任用户ID[{}]仍在其他班担任班主任,保留角色", oldTeacherId);
}
}
}
// 4. 插入新的班主任关联记录
@@ -248,7 +248,9 @@ public class XgStudentManagerSourceServiceImpl implements IXgStudentManagerSourc
// 非全校学生管理员:使用原有逻辑
// 根据当前用户的id,从班级表中,获取当前用户对应的班级id,也加入到authSourceIds中
String classSql = "SELECT id FROM xg_class_msg WHERE class_teacher_id = ? AND deleted = '0' AND class_status = '1'";
// String classSql = "SELECT id FROM xg_class_msg WHERE class_teacher_id = ? AND
// deleted = '0' AND class_status = '1'";
String classSql = "SELECT id FROM xg_class_msg WHERE class_teacher_id = ? AND deleted = '0' ";
List<Long> classIds = jdbcTemplate.queryForList(classSql, Long.class, currentUserId);
// if (classIds.isEmpty()) {
// return Collections.emptyList();
@@ -310,7 +312,8 @@ public class XgStudentManagerSourceServiceImpl implements IXgStudentManagerSourc
// 查询所有部门信息
String inSql = finalDeptIds.stream().map(String::valueOf).collect(Collectors.joining(","));
String sql = "SELECT dept_id, dept_name, parent_id, dept_category, order_num FROM sys_dept " +
"WHERE dept_id IN (" + inSql + ") AND status = '0' AND del_flag = '0' ORDER BY order_num, create_time DESC";
"WHERE dept_id IN (" + inSql
+ ") AND status = '0' AND del_flag = '0' ORDER BY order_num, create_time DESC";
List<Map<String, Object>> deptMaps = jdbcTemplate.queryForList(sql);
// 构建部门树
@@ -301,8 +301,7 @@ public class XgStudentMsgServiceImpl implements IXgStudentMsgService {
// 第四步:通过xg_class_msg表,class_teacher_id=当前登录人id,deleted='0',class_status='1',获取id
LambdaQueryWrapper<XgClassMsg> classWrapper = Wrappers.lambdaQuery();
classWrapper.eq(XgClassMsg::getClassTeacherId, userId)
.eq(XgClassMsg::getDeleted, 0L)
.eq(XgClassMsg::getClassStatus, "1");
.eq(XgClassMsg::getDeleted, 0L);
List<XgClassMsg> classes = xgClassMsgMapper.selectList(classWrapper);
for (XgClassMsg classMsg : classes) {
@@ -0,0 +1,677 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.xg.mapper.IXgConductScoreMapper">
<resultMap type="org.dromara.xg.domain.vo.XgConductScoreVo" id="XgConductScoreResult">
<result property="id" column="id"/>
<result property="tenantId" column="tenant_id"/>
<result property="academicYear" column="academic_year"/>
<result property="semester" column="semester"/>
<result property="month" column="month"/>
<result property="studentId" column="student_id"/>
<result property="studentName" column="student_name"/>
<result property="studentNo" column="student_no"/>
<result property="classId" column="class_id"/>
<result property="className" column="class_name"/>
<result property="classTeacherId" column="class_teacher_id"/>
<result property="classTeacherName" column="class_teacher_name"/>
<result property="score" column="score"/>
<result property="remark" column="remark"/>
<result property="createTime" column="create_time"/>
<result property="createBy" column="create_by"/>
<result property="createByName" column="create_by_name"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
<resultMap type="org.dromara.xg.domain.vo.XgConductScoreClassSummaryVo" id="XgConductScoreClassSummaryResult">
<collection property="classes" ofType="org.dromara.xg.domain.vo.XgConductScoreClassVo">
<result property="classId" column="class_id"/>
<result property="className" column="class_name"/>
<result property="classCode" column="class_code"/>
<result property="studentCount" column="student_count"/>
<result property="enrollmentYear" column="enrollment_year"/>
<result property="majorName" column="major_name"/>
<collection property="students" ofType="org.dromara.xg.domain.vo.XgConductScoreClassStudentVo">
<result property="studentId" column="student_id"/>
<result property="studentNo" column="student_no"/>
<result property="studentName" column="student_name"/>
<result property="gender" column="gender"/>
<result property="lastScore" column="last_score"/>
<result property="lastMonth" column="last_month"/>
<association property="existingScore" javaType="org.dromara.xg.domain.vo.XgConductScoreDetailVo">
<result property="id" column="score_id"/>
<result property="score" column="existing_score"/>
<result property="remark" column="existing_remark"/>
<result property="createTime" column="existing_create_time"/>
</association>
</collection>
</collection>
</resultMap>
<resultMap type="org.dromara.xg.domain.vo.XgConductScoreSemesterAverageVo" id="XgConductScoreSemesterAverageResult">
<result property="studentId" column="student_id"/>
<result property="studentNo" column="student_no"/>
<result property="studentName" column="student_name"/>
<result property="gender" column="gender"/>
<result property="classId" column="class_id"/>
<result property="className" column="class_name"/>
<result property="academicYear" column="academic_year"/>
<result property="semester" column="semester"/>
<result property="avgScore" column="avg_score"/>
<result property="recordCount" column="record_count"/>
<result property="maxScore" column="max_score"/>
<result property="minScore" column="min_score"/>
<result property="scoreRange" column="score_range"/>
<result property="lastUpdateTime" column="last_update_time"/>
</resultMap>
<sql id="selectXgConductScoreVo">
SELECT cs.id,
cs.tenant_id,
cs.academic_year,
cs.semester,
cs.month,
cs.student_id,
s.name AS student_name,
s.student_no,
cs.class_id,
c.class_name,
c.college_name,
c.major_name,
cs.class_teacher_id,
u.nick_name AS class_teacher_name,
cs.score,
cs.remark,
cs.create_time,
cs.create_by,
cu.nick_name AS create_by_name,
cs.update_by,
cs.update_time
FROM xg_conduct_score cs
LEFT JOIN xg_student_msg s ON cs.student_id = s.id
LEFT JOIN xg_class_msg c ON cs.class_id = c.id
LEFT JOIN sys_user u ON cs.class_teacher_id = u.user_id
LEFT JOIN sys_user cu ON cs.create_by = cu.user_id
</sql>
<select id="selectXgConductScoreList" parameterType="org.dromara.xg.domain.bo.XgConductScoreQueryBo" resultMap="XgConductScoreResult">
<include refid="selectXgConductScoreVo"/>
<where>
<if test="tenantId != null and tenantId != ''">
AND cs.tenant_id = #{tenantId}
</if>
<if test="academicYear != null">
AND cs.academic_year = #{academicYear}
</if>
<if test="semester != null">
AND cs.semester = #{semester}
</if>
<if test="month != null">
AND cs.month = #{month}
</if>
<if test="studentId != null">
AND cs.student_id = #{studentId}
</if>
<if test="classId != null">
AND cs.class_id = #{classId}
</if>
<if test="classTeacherId != null">
AND cs.class_teacher_id = #{classTeacherId}
</if>
<if test="studentName != null and studentName != ''">
AND s.name LIKE CONCAT('%', #{studentName}, '%')
</if>
<if test="studentNo != null and studentNo != ''">
AND s.student_no LIKE CONCAT('%', #{studentNo}, '%')
</if>
<if test="className != null and className != ''">
AND c.class_name LIKE CONCAT('%', #{className}, '%')
</if>
<if test="collegeName != null and collegeName != ''">
AND c.college_name LIKE CONCAT('%', #{collegeName}, '%')
</if>
<if test="majorName != null and majorName != ''">
AND c.major_name LIKE CONCAT('%', #{majorName}, '%')
</if>
<if test="scoreMin != null">
AND cs.score >= #{scoreMin}
</if>
<if test="scoreMax != null">
AND cs.score <![CDATA[<=]]> #{scoreMax}
</if>
AND cs.deleted = 0
AND s.deleted = 0
AND c.deleted = 0
</where>
<!-- 排序由 PageQuery 处理 -->
</select>
<select id="selectPageByDeptIds" resultMap="XgConductScoreResult">
<include refid="selectXgConductScoreVo"/>
<where>
<!-- 租户ID条件 -->
<if test="bo.tenantId != null and bo.tenantId != ''">
cs.tenant_id = #{bo.tenantId}
</if>
<if test="bo.tenantId == null or bo.tenantId == ''">
cs.tenant_id = '000000'
</if>
<!-- 学年条件 -->
<if test="bo.academicYear != null">
AND cs.academic_year = #{bo.academicYear}
</if>
<!-- 学期条件 -->
<if test="bo.semester != null">
AND cs.semester = #{bo.semester}
</if>
<!-- 月份条件 -->
<if test="bo.month != null">
AND cs.month = #{bo.month}
</if>
<!-- 学生ID条件 -->
<if test="bo.studentId != null">
AND cs.student_id = #{bo.studentId}
</if>
<!-- 班级ID条件 -->
<if test="bo.classId != null">
AND cs.class_id = #{bo.classId}
</if>
<!-- 班主任ID条件 -->
<if test="bo.classTeacherId != null">
AND cs.class_teacher_id = #{bo.classTeacherId}
</if>
<!-- 学生姓名条件 -->
<if test="bo.studentName != null and bo.studentName != ''">
AND s.name LIKE CONCAT('%', #{bo.studentName}, '%')
</if>
<!-- 学号条件 -->
<if test="bo.studentNo != null and bo.studentNo != ''">
AND s.student_no LIKE CONCAT('%', #{bo.studentNo}, '%')
</if>
<!-- 学院名称条件 -->
<if test="bo.collegeName != null and bo.collegeName != ''">
AND c.college_name LIKE CONCAT('%', #{bo.collegeName}, '%')
</if>
<!-- 专业名称条件 -->
<if test="bo.majorName != null and bo.majorName != ''">
AND c.major_name LIKE CONCAT('%', #{bo.majorName}, '%')
</if>
<!-- 分数范围条件 -->
<if test="bo.scoreMin != null">
AND cs.score >= #{bo.scoreMin}
</if>
<if test="bo.scoreMax != null">
AND cs.score <![CDATA[<=]]> #{bo.scoreMax}
</if>
<!-- 时间范围条件 -->
<if test="bo.beginTime != null and bo.beginTime != ''">
AND cs.create_time >= #{bo.beginTime}
</if>
<if test="bo.endTime != null and bo.endTime != ''">
AND cs.create_time <![CDATA[<=]]> #{bo.endTime}
</if>
<!-- 班级ID列表过滤条件 -->
<if test="classIdList != null and classIdList.size() > 0">
AND cs.class_id IN
<foreach collection="classIdList" item="classId" open="(" close=")" separator=",">
#{classId}
</foreach>
</if>
<!-- 删除标记条件 -->
AND cs.deleted = 0
AND s.deleted = 0
AND c.deleted = 0
</where>
<!-- 排序由 PageQuery 处理 -->
</select>
<select id="selectListByDeptIds" resultMap="XgConductScoreResult">
<include refid="selectXgConductScoreVo"/>
<where>
<!-- 租户ID条件 -->
<if test="bo.tenantId != null and bo.tenantId != ''">
cs.tenant_id = #{bo.tenantId}
</if>
<if test="bo.tenantId == null or bo.tenantId == ''">
cs.tenant_id = '000000'
</if>
<!-- 学年条件 -->
<if test="bo.academicYear != null">
AND cs.academic_year = #{bo.academicYear}
</if>
<!-- 学期条件 -->
<if test="bo.semester != null">
AND cs.semester = #{bo.semester}
</if>
<!-- 月份条件 -->
<if test="bo.month != null">
AND cs.month = #{bo.month}
</if>
<!-- 学生ID条件 -->
<if test="bo.studentId != null">
AND cs.student_id = #{bo.studentId}
</if>
<!-- 班级ID条件 -->
<if test="bo.classId != null">
AND cs.class_id = #{bo.classId}
</if>
<!-- 班主任ID条件 -->
<if test="bo.classTeacherId != null">
AND cs.class_teacher_id = #{bo.classTeacherId}
</if>
<!-- 学生姓名条件 -->
<if test="bo.studentName != null and bo.studentName != ''">
AND s.name LIKE CONCAT('%', #{bo.studentName}, '%')
</if>
<!-- 学号条件 -->
<if test="bo.studentNo != null and bo.studentNo != ''">
AND s.student_no LIKE CONCAT('%', #{bo.studentNo}, '%')
</if>
<!-- 学院名称条件 -->
<if test="bo.collegeName != null and bo.collegeName != ''">
AND c.college_name LIKE CONCAT('%', #{bo.collegeName}, '%')
</if>
<!-- 专业名称条件 -->
<if test="bo.majorName != null and bo.majorName != ''">
AND c.major_name LIKE CONCAT('%', #{bo.majorName}, '%')
</if>
<!-- 分数范围条件 -->
<if test="bo.scoreMin != null">
AND cs.score >= #{bo.scoreMin}
</if>
<if test="bo.scoreMax != null">
AND cs.score <![CDATA[<=]]> #{bo.scoreMax}
</if>
<!-- 时间范围条件 -->
<if test="bo.beginTime != null and bo.beginTime != ''">
AND cs.create_time >= #{bo.beginTime}
</if>
<if test="bo.endTime != null and bo.endTime != ''">
AND cs.create_time <![CDATA[<=]]> #{bo.endTime}
</if>
<!-- 班级ID列表过滤条件 -->
<if test="classIdList != null and classIdList.size() > 0">
AND cs.class_id IN
<foreach collection="classIdList" item="classId" open="(" close=")" separator=",">
#{classId}
</foreach>
</if>
<!-- 删除标记条件 -->
AND cs.deleted = 0
AND s.deleted = 0
AND c.deleted = 0
</where>
<!-- 排序由 PageQuery 处理 -->
</select>
<select id="selectClassStudentsByTeacher" parameterType="java.util.Map" resultType="java.util.Map">
SELECT
tc.class_id,
tc.class_name,
tc.class_code,
tc.student_count,
tc.enrollment_year,
tc.grade,
NULL AS major_name,
s.id AS student_id,
s.student_no,
s.name AS student_name,
s.gender,
cs_last.score AS last_score,
cs_last.month AS last_month,
cs_current.id AS score_id,
cs_current.score AS existing_score,
cs_current.remark AS existing_remark,
cs_current.create_time AS existing_create_time
FROM (
SELECT
c.id AS class_id,
c.class_name,
c.class_code,
c.enrollment_year,
c.grade,
COUNT(s.id) AS student_count
FROM xg_class_msg c
LEFT JOIN xg_student_msg s ON c.id = s.class_id AND s.deleted = 0
WHERE c.class_teacher_id = #{teacherId}
AND c.deleted = 0
GROUP BY c.id, c.class_name, c.class_code, c.enrollment_year, c.grade
) tc
INNER JOIN xg_student_msg s ON tc.class_id = s.class_id AND s.deleted = 0
LEFT JOIN xg_conduct_score cs_current ON s.id = cs_current.student_id
AND cs_current.academic_year = #{academicYear}
AND cs_current.semester = #{semester}
AND cs_current.month = #{month}
AND cs_current.deleted = 0
LEFT JOIN (
SELECT
cs_inner.student_id,
cs_inner.score,
cs_inner.month
FROM xg_conduct_score cs_inner
INNER JOIN (
SELECT
student_id,
MAX(CONCAT(LPAD(academic_year, 4, '0'), LPAD(semester, 1, '0'), LPAD(month, 2, '0'))) AS max_date
FROM xg_conduct_score
WHERE academic_year = #{academicYear}
AND semester = #{semester}
AND month <![CDATA[<]]> #{month}
AND deleted = 0
GROUP BY student_id
) latest ON cs_inner.student_id = latest.student_id
AND CONCAT(LPAD(cs_inner.academic_year, 4, '0'), LPAD(cs_inner.semester, 1, '0'), LPAD(cs_inner.month, 2, '0')) = latest.max_date
AND cs_inner.deleted = 0
) cs_last ON s.id = cs_last.student_id
ORDER BY tc.class_name, s.student_no
</select>
<select id="selectSemesterAverageList" parameterType="org.dromara.xg.domain.bo.XgConductScoreQueryBo" resultMap="XgConductScoreSemesterAverageResult">
SELECT
cs.student_id,
s.student_no,
s.name AS student_name,
s.gender,
cs.class_id,
c.class_name,
cs.academic_year,
cs.semester,
ROUND(AVG(cs.score), 2) AS avg_score,
COUNT(*) AS record_count,
MAX(cs.score) AS max_score,
MIN(cs.score) AS min_score,
CONCAT(MIN(cs.score), '-', MAX(cs.score)) AS score_range,
MAX(cs.update_time) AS last_update_time
FROM xg_conduct_score cs
LEFT JOIN xg_student_msg s ON cs.student_id = s.id
LEFT JOIN xg_class_msg c ON cs.class_id = c.id
<where>
<if test="tenantId != null and tenantId != ''">
AND cs.tenant_id = #{tenantId}
</if>
<if test="academicYear != null">
AND cs.academic_year = #{academicYear}
</if>
<if test="semester != null">
AND cs.semester = #{semester}
</if>
<if test="classId != null">
AND cs.class_id = #{classId}
</if>
<if test="studentName != null and studentName != ''">
AND s.name LIKE CONCAT('%', #{studentName}, '%')
</if>
<if test="className != null and className != ''">
AND c.class_name LIKE CONCAT('%', #{className}, '%')
</if>
AND cs.deleted = 0
AND s.deleted = 0
AND c.deleted = 0
</where>
GROUP BY cs.student_id, cs.academic_year, cs.semester
ORDER BY c.class_name, s.student_no
</select>
<select id="selectSemesterAverageListByDeptIds" parameterType="org.dromara.xg.domain.bo.XgConductScoreQueryBo" resultMap="XgConductScoreSemesterAverageResult">
SELECT
s.id AS student_id,
s.student_no,
s.name AS student_name,
s.gender,
s.class_id,
c.class_name,
c.college_name,
c.major_name,
#{queryBo.academicYear} AS academic_year,
#{queryBo.semester} AS semester,
COALESCE(ROUND(AVG(cs.score), 2), 0) AS avg_score,
COALESCE(COUNT(cs.id), 0) AS record_count,
COALESCE(MAX(cs.score), 0) AS max_score,
COALESCE(MIN(cs.score), 0) AS min_score,
CASE
WHEN COUNT(cs.id) > 0 THEN CONCAT(COALESCE(MIN(cs.score), 0), '-', COALESCE(MAX(cs.score), 0))
ELSE '0-0'
END AS score_range,
MAX(cs.update_time) AS last_update_time
FROM xg_student_msg s
LEFT JOIN xg_class_msg c ON s.class_id = c.id
LEFT JOIN xg_conduct_score cs ON s.id = cs.student_id
AND cs.academic_year = #{queryBo.academicYear}
AND cs.semester = #{queryBo.semester}
AND cs.deleted = 0
<where>
<if test="queryBo.tenantId != null and queryBo.tenantId != ''">
AND s.tenant_id = #{queryBo.tenantId}
</if>
<if test="classIdList != null and classIdList.size() > 0">
AND s.class_id IN
<foreach collection="classIdList" item="classId" open="(" separator="," close=")">
#{classId}
</foreach>
</if>
<if test="queryBo.studentName != null and queryBo.studentName != ''">
AND s.name LIKE CONCAT('%', #{queryBo.studentName}, '%')
</if>
<if test="queryBo.className != null and queryBo.className != ''">
AND c.class_name LIKE CONCAT('%', #{queryBo.className}, '%')
</if>
<if test="queryBo.collegeName != null and queryBo.collegeName != ''">
AND c.college_name LIKE CONCAT('%', #{queryBo.collegeName}, '%')
</if>
<if test="queryBo.majorName != null and queryBo.majorName != ''">
AND c.major_name LIKE CONCAT('%', #{queryBo.majorName}, '%')
</if>
AND s.deleted = 0
AND c.deleted = 0
</where>
GROUP BY s.id, s.student_no, s.name, s.gender, s.class_id, c.class_name, c.college_name, c.major_name
ORDER BY s.student_no DESC, s.create_time DESC
</select>
<select id="existsByStudentAndMonth" resultType="boolean">
SELECT COUNT(1) > 0
FROM xg_conduct_score
WHERE student_id = #{studentId}
AND academic_year = #{academicYear}
AND semester = #{semester}
AND month = #{month}
AND tenant_id = #{tenantId}
AND deleted = 0
</select>
<insert id="insertBatch" parameterType="java.util.List">
INSERT INTO xg_conduct_score (
id, tenant_id, academic_year, semester, month,
student_id, class_id, class_teacher_id, score, remark,
create_dept, create_by, create_time
) VALUES
<foreach collection="list" item="item" separator=",">
(
#{item.id}, #{item.tenantId}, #{item.academicYear}, #{item.semester}, #{item.month},
#{item.studentId}, #{item.classId}, #{item.classTeacherId}, #{item.score}, #{item.remark},
#{item.createDept}, #{item.createBy}, #{item.createTime}
)
</foreach>
</insert>
<select id="selectTeacherClassIds" parameterType="Long" resultType="Long">
SELECT DISTINCT c.id
FROM xg_class_msg c
WHERE c.class_teacher_id = #{teacherId}
AND c.deleted = 0
</select>
<select id="checkTeacherClass" resultType="boolean">
SELECT COUNT(1) > 0
FROM xg_class_msg
WHERE id = #{classId}
AND class_teacher_id = #{teacherId}
AND deleted = 0
</select>
<select id="selectStudentRecordsRaw" parameterType="java.util.Map" resultType="java.util.Map">
SELECT
s.id AS student_id,
s.student_no,
s.name AS student_name,
s.gender,
c.id AS class_id,
c.class_name,
c.class_teacher_id,
u.nick_name AS class_teacher_name,
stats.semester_average,
stats.total_records,
cs.id,
cs.academic_year,
cs.semester,
cs.month,
cs.score,
cs.remark,
cs.create_by,
u1.nick_name AS create_by_name,
cs.create_time,
cs.update_by,
u2.nick_name AS update_by_name,
cs.update_time
FROM xg_student_msg s
LEFT JOIN xg_class_msg c ON s.class_id = c.id AND c.deleted = 0 AND c.tenant_id = #{tenantId}
LEFT JOIN sys_user u ON c.class_teacher_id = u.user_id
LEFT JOIN (
SELECT
cs_inner.student_id,
ROUND(AVG(cs_inner.score), 2) AS semester_average,
COUNT(cs_inner.id) AS total_records
FROM xg_conduct_score cs_inner
WHERE cs_inner.tenant_id = #{tenantId}
AND cs_inner.deleted = 0
<if test="academicYear != null">
AND cs_inner.academic_year = #{academicYear}
</if>
<if test="semester != null">
AND cs_inner.semester = #{semester}
</if>
GROUP BY cs_inner.student_id
) stats ON s.id = stats.student_id
LEFT JOIN xg_conduct_score cs ON s.id = cs.student_id
AND cs.tenant_id = #{tenantId}
AND cs.deleted = 0
<if test="academicYear != null">
AND cs.academic_year = #{academicYear}
</if>
<if test="semester != null">
AND cs.semester = #{semester}
</if>
LEFT JOIN sys_user u1 ON cs.create_by = u1.user_id
LEFT JOIN sys_user u2 ON cs.update_by = u2.user_id
WHERE s.id = #{studentId}
AND s.tenant_id = #{tenantId}
AND s.deleted = 0
ORDER BY cs.academic_year DESC, cs.semester DESC, cs.month DESC, cs.create_time DESC
</select>
<select id="selectClassDetailStatistics" parameterType="java.util.Map" resultMap="XgConductScoreClassDetailResult">
SELECT
#{classId} AS class_id,
c.class_name,
#{academicYear} AS academic_year,
#{semester} AS semester,
COUNT(DISTINCT s.id) AS total_students,
COUNT(DISTINCT CASE WHEN cs_stats.record_count > 0 THEN cs_stats.student_id END) AS recorded_students,
ROUND(
COUNT(DISTINCT CASE WHEN cs_stats.record_count > 0 THEN cs_stats.student_id END) * 100.0 /
NULLIF(COUNT(DISTINCT s.id), 0),
2
) AS record_rate,
ROUND(AVG(cs_stats.avg_score), 2) AS class_average,
MAX(cs_stats.avg_score) AS max_score,
MIN(cs_stats.avg_score) AS min_score,
cs_stats.student_id,
cs_stats.student_no,
cs_stats.student_name,
cs_stats.gender,
cs_stats.avg_score AS student_avg_score,
cs_stats.record_count
FROM xg_class_msg c
LEFT JOIN xg_student_msg s ON c.id = s.class_id
AND s.tenant_id = #{tenantId}
AND s.deleted = 0
LEFT JOIN (
SELECT
s_inner.id AS student_id,
s_inner.student_no,
s_inner.name AS student_name,
s_inner.gender,
ROUND(AVG(cs.score), 2) AS avg_score,
COUNT(cs.id) AS record_count
FROM xg_student_msg s_inner
LEFT JOIN xg_conduct_score cs ON s_inner.id = cs.student_id
AND cs.academic_year = #{academicYear}
AND cs.semester = #{semester}
AND cs.tenant_id = #{tenantId}
AND cs.deleted = 0
WHERE s_inner.class_id = #{classId}
AND s_inner.tenant_id = #{tenantId}
AND s_inner.deleted = 0
GROUP BY s_inner.id, s_inner.student_no, s_inner.name, s_inner.gender
) cs_stats ON s.id = cs_stats.student_id
WHERE c.id = #{classId}
AND c.tenant_id = #{tenantId}
AND c.deleted = 0
GROUP BY c.id, c.class_name,
cs_stats.student_id, cs_stats.student_no, cs_stats.student_name, cs_stats.gender,
cs_stats.avg_score, cs_stats.record_count
</select>
<!-- 结果映射 -->
<resultMap type="org.dromara.xg.domain.vo.XgConductScoreStudentRecordsVo" id="XgConductScoreStudentRecordsResult">
<id property="studentId" column="student_id"/>
<result property="studentNo" column="student_no"/>
<result property="studentName" column="student_name"/>
<result property="gender" column="gender"/>
<result property="classId" column="class_id"/>
<result property="className" column="class_name"/>
<result property="classTeacherId" column="class_teacher_id"/>
<result property="classTeacherName" column="class_teacher_name"/>
<result property="semesterAverage" column="semester_average"/>
<result property="totalRecords" column="total_records"/>
<collection property="records" ofType="org.dromara.xg.domain.vo.XgConductScoreRecordDetailVo">
<result property="id" column="id"/>
<result property="academicYear" column="academic_year"/>
<result property="semester" column="semester"/>
<result property="month" column="month"/>
<result property="score" column="score"/>
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createByName" column="create_by_name"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateByName" column="update_by_name"/>
<result property="updateTime" column="update_time"/>
</collection>
</resultMap>
<resultMap type="org.dromara.xg.domain.vo.XgConductScoreClassDetailVo" id="XgConductScoreClassDetailResult">
<result property="classId" column="class_id"/>
<result property="className" column="class_name"/>
<result property="academicYear" column="academic_year"/>
<result property="semester" column="semester"/>
<result property="totalStudents" column="total_students"/>
<result property="recordedStudents" column="recorded_students"/>
<result property="recordRate" column="record_rate"/>
<result property="classAverage" column="class_average"/>
<result property="maxScore" column="max_score"/>
<result property="minScore" column="min_score"/>
<collection property="students" ofType="org.dromara.xg.domain.vo.XgConductScoreClassStudentDetailVo">
<result property="studentId" column="student_id"/>
<result property="studentNo" column="student_no"/>
<result property="studentName" column="student_name"/>
<result property="gender" column="gender"/>
<result property="avgScore" column="avg_score"/>
<result property="recordCount" column="record_count"/>
</collection>
</resultMap>
</mapper>