diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java index 2e39137e..6399c816 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipDailyController.java @@ -99,4 +99,22 @@ public class ZsjyInternshipDailyController extends BaseController { return dailyService.getStatistics(queryBo); } + /** + * 获取学生个人月度考勤统计(移动端专用) + */ + @Operation(summary = "获取学生月度统计", description = "获取指定学生的月度考勤统计数据") + @SaCheckPermission("zsjy:internshipdaily:statistics") + @GetMapping("/monthly-statistics") + public R> getStudentMonthlyStatistics( + @Parameter(description = "学生ID", required = true) @RequestParam Long studentId, + @Parameter(description = "年份", required = true) @RequestParam Integer year, + @Parameter(description = "月份", required = true) @RequestParam Integer month) { + + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/daily/monthly-statistics"); + log.info("学生ID: {}, 年月: {}-{}", studentId, year, month); + + return dailyService.getStudentMonthlyStatistics(studentId, year, month); + } + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipVisitController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipVisitController.java new file mode 100644 index 00000000..b5da4fc6 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipVisitController.java @@ -0,0 +1,131 @@ +package org.dromara.zsjy.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; +import org.dromara.zsjy.service.IZsjyInternshipVisitService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 班主任走访Controller + * + * @author system + * @date 2025-01-03 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/zsjy/internshipVisit") +@Tag(name = "班主任走访管理", description = "班主任走访管理") +public class ZsjyInternshipVisitController extends BaseController { + + private final IZsjyInternshipVisitService zsjyInternshipVisitService; + + /** + * 查询班主任走访列表 + */ + @Operation(summary = "查询班主任走访列表") + @SaCheckPermission("zsjy:internshipVisit:list") + @GetMapping("/list") + public TableDataInfo list(ZsjyInternshipVisitQueryBo bo, PageQuery pageQuery) { + return zsjyInternshipVisitService.queryPageList(bo, pageQuery); + } + + /** + * 导出班主任走访列表 + */ + @Operation(summary = "导出班主任走访列表") + @SaCheckPermission("zsjy:internshipVisit:export") + @Log(title = "班主任走访", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(ZsjyInternshipVisitQueryBo bo, HttpServletResponse response) { + List list = zsjyInternshipVisitService.queryList(bo); + ExcelUtil.exportExcel(list, "班主任走访", ZsjyInternshipVisitVo.class, response); + } + + /** + * 获取班主任走访详细信息 + */ + @Operation(summary = "获取班主任走访详细信息") + @SaCheckPermission("zsjy:internshipVisit:query") + @GetMapping("/{visitId}") + @Parameters({ + @Parameter(name = "visitId", description = "走访记录ID", required = true) + }) + public R getInfo(@NotNull(message = "走访记录ID不能为空") + @PathVariable Long visitId) { + return R.ok(zsjyInternshipVisitService.queryById(visitId)); + } + + /** + * 新增班主任走访 + */ + @Operation(summary = "新增班主任走访") + @SaCheckPermission("zsjy:internshipVisit:add") + @Log(title = "班主任走访", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody ZsjyInternshipVisitBo bo) { + return toAjax(zsjyInternshipVisitService.insertByBo(bo)); + } + + /** + * 修改班主任走访 + */ + @Operation(summary = "修改班主任走访") + @SaCheckPermission("zsjy:internshipVisit:edit") + @Log(title = "班主任走访", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody ZsjyInternshipVisitBo bo) { + return toAjax(zsjyInternshipVisitService.updateByBo(bo)); + } + + /** + * 删除班主任走访 + */ + @Operation(summary = "删除班主任走访") + @SaCheckPermission("zsjy:internshipVisit:remove") + @Log(title = "班主任走访", businessType = BusinessType.DELETE) + @DeleteMapping("/{visitIds}") + @Parameters({ + @Parameter(name = "visitIds", description = "走访记录ID串", required = true) + }) + public R remove(@NotEmpty(message = "走访记录ID不能为空") + @PathVariable Long[] visitIds) { + return toAjax(zsjyInternshipVisitService.deleteWithValidByIds(List.of(visitIds), true)); + } + + /** + * 获取可选项列表(学生选择) + */ + @Operation(summary = "获取可选项列表") + @SaCheckPermission("zsjy:internshipVisit:list") + @GetMapping("/options") + public R> getOptions() { + List list = zsjyInternshipVisitService.getStudentOptions(); + return R.ok(list); + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipVisit.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipVisit.java new file mode 100644 index 00000000..b09fc6a7 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipVisit.java @@ -0,0 +1,173 @@ +package org.dromara.zsjy.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 班主任走访实体对象 zsjy_internship_visit + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("zsjy_internship_visit") +public class ZsjyInternshipVisit extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 走访记录ID + */ + @TableId(value = "visit_id", type = IdType.ASSIGN_ID) + private Long visitId; + + /** + * 租户ID + */ + @TableField("tenant_id") + private String tenantId; + + /** + * 实习ID + */ + @TableField("internship_id") + private Long internshipId; + + /** + * 学生ID + */ + @TableField("student_id") + private Long studentId; + + /** + * 企业名称 + */ + @TableField("company_name") + private String companyName; + + /** + * 企业地址 + */ + @TableField("company_address") + private String companyAddress; + + /** + * 走访时间 + */ + @TableField("visit_date") + private Date visitDate; + + /** + * 走访人员 + */ + @TableField("visitor_name") + private String visitorName; + + /** + * 走访人员职务 + */ + @TableField("visitor_position") + private String visitorPosition; + + /** + * 走访目的 + */ + @TableField("visit_purpose") + private String visitPurpose; + + /** + * 企业情况 + */ + @TableField("company_situation") + private String companySituation; + + /** + * 学生表现 + */ + @TableField("student_performance") + private String studentPerformance; + + /** + * 发现问题 + */ + @TableField("problems_found") + private String problemsFound; + + /** + * 改进建议 + */ + @TableField("suggestions") + private String suggestions; + + /** + * 走访总结 + */ + @TableField("visit_summary") + private String visitSummary; + + /** + * 现场照片(JSON格式) + */ + @TableField("visit_images") + private String visitImages; + + /** + * 走访文件附件(JSON格式) + */ + @TableField("visit_files") + private String visitFiles; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核) + */ + @TableField("status") + private String status; + + /** + * 备注 + */ + @TableField("remark") + private String remark; + + // ==================== 扩展字段(数据库中不存在,通过关联查询获取) ==================== + + /** + * 学生姓名 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String studentName; + + /** + * 学号 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String studentNumber; + + /** + * 班级名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String className; + + /** + * 专业名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String majorName; + + /** + * 学院名称 (通过student_id关联查询获取) + */ + @TableField(exist = false) + private String collegeName; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java index 6f0f040c..648015fb 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipDailyQueryBo.java @@ -82,4 +82,10 @@ public class ZsjyInternshipDailyQueryBo extends BaseEntity { @Schema(description = "公司名称") private String companyName; + /** + * 班主任ID(用于权限控制) + */ + @Schema(description = "班主任ID") + private Long headTeacherId; + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitBo.java new file mode 100644 index 00000000..32272bb0 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitBo.java @@ -0,0 +1,131 @@ +package org.dromara.zsjy.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +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 org.dromara.zsjy.domain.ZsjyInternshipVisit; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import java.util.Date; + +/** + * 班主任走访业务对象 ZsjyInternshipVisitBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = ZsjyInternshipVisit.class, reverseConvertGenerate = false) +public class ZsjyInternshipVisitBo extends BaseEntity { + + /** + * 走访记录ID + */ + @NotNull(message = "走访记录ID不能为空", groups = { EditGroup.class }) + private Long visitId; + + /** + * 租户编号 + */ + private String tenantId; + + /** + * 实习ID + */ + @NotNull(message = "实习ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long internshipId; + + /** + * 学生ID + */ + @NotNull(message = "学生ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long studentId; + + /** + * 企业名称 + */ + @NotBlank(message = "企业名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String companyName; + + /** + * 企业地址 + */ + private String companyAddress; + + /** + * 走访时间 + */ + @NotNull(message = "走访时间不能为空", groups = { AddGroup.class, EditGroup.class }) + private Date visitDate; + + /** + * 走访人员 + */ + @NotBlank(message = "走访人员不能为空", groups = { AddGroup.class, EditGroup.class }) + private String visitorName; + + /** + * 走访人员职务 + */ + private String visitorPosition; + + /** + * 走访目的 + */ + @NotBlank(message = "走访目的不能为空", groups = { AddGroup.class, EditGroup.class }) + private String visitPurpose; + + /** + * 企业情况 + */ + private String companySituation; + + /** + * 学生表现 + */ + private String studentPerformance; + + /** + * 发现问题 + */ + private String problemsFound; + + /** + * 改进建议 + */ + private String suggestions; + + /** + * 走访总结 + */ + private String visitSummary; + + /** + * 现场照片(JSON格式) + */ + private String visitImages; + + /** + * 走访文件附件(JSON格式) + */ + private String visitFiles; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核) + */ + @Pattern(regexp = "^[0-2]$", + message = "走访状态只能是:0(草稿)、1(已提交)、2(已审核)", + groups = { AddGroup.class, EditGroup.class }) + private String status; + + /** + * 备注 + */ + private String remark; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitQueryBo.java new file mode 100644 index 00000000..8c656c92 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipVisitQueryBo.java @@ -0,0 +1,86 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.util.Date; + +/** + * 班主任走访查询业务对象 ZsjyInternshipVisitQueryBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "班主任走访查询业务对象") +public class ZsjyInternshipVisitQueryBo extends BaseEntity { + + /** + * 走访记录ID + */ + @Schema(description = "走访记录ID") + private Long visitId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @Schema(description = "学号") + private String studentNumber; + + /** + * 班级名称 + */ + @Schema(description = "班级名称") + private String className; + + /** + * 企业名称 + */ + @Schema(description = "企业名称") + private String companyName; + + /** + * 走访人员 + */ + @Schema(description = "走访人员") + private String visitorName; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核),前端通过字典显示为中文 + */ + @Schema(description = "走访状态") + private String status; + + /** + * 走访开始时间 + */ + @Schema(description = "走访开始时间") + private Date visitStartDate; + + /** + * 走访结束时间 + */ + @Schema(description = "走访结束时间") + private Date visitEndDate; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipVisitVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipVisitVo.java new file mode 100644 index 00000000..546dc88a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipVisitVo.java @@ -0,0 +1,201 @@ +package org.dromara.zsjy.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 班主任走访视图对象 ZsjyInternshipVisitVo + * + * @author system + * @date 2025-01-03 + */ +@Data +@AutoMapper(target = ZsjyInternshipVisit.class) +@Schema(description = "班主任走访视图对象") +public class ZsjyInternshipVisitVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 走访记录ID + */ + @Schema(description = "走访记录ID") + private Long visitId; + + /** + * 租户编号 + */ + @Schema(description = "租户编号") + private String tenantId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 企业名称 + */ + @Schema(description = "企业名称") + private String companyName; + + /** + * 企业地址 + */ + @Schema(description = "企业地址") + private String companyAddress; + + /** + * 走访时间 + */ + @Schema(description = "走访时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date visitDate; + + /** + * 走访人员 + */ + @Schema(description = "走访人员") + private String visitorName; + + /** + * 走访人员职务 + */ + @Schema(description = "走访人员职务") + private String visitorPosition; + + /** + * 走访目的 + */ + @Schema(description = "走访目的") + private String visitPurpose; + + /** + * 企业情况 + */ + @Schema(description = "企业情况") + private String companySituation; + + /** + * 学生表现 + */ + @Schema(description = "学生表现") + private String studentPerformance; + + /** + * 发现问题 + */ + @Schema(description = "发现问题") + private String problemsFound; + + /** + * 改进建议 + */ + @Schema(description = "改进建议") + private String suggestions; + + /** + * 走访总结 + */ + @Schema(description = "走访总结") + private String visitSummary; + + /** + * 现场照片(JSON格式) + */ + @Schema(description = "现场照片") + private String visitImages; + + /** + * 走访文件附件(JSON格式) + */ + @Schema(description = "走访文件附件") + private String visitFiles; + + /** + * 走访状态:数据库存储数字编码(0-草稿 1-已提交 2-已审核),前端通过字典显示为中文 + */ + @Schema(description = "走访状态") + private String status; + + /** + * 备注 + */ + @Schema(description = "备注") + private String remark; + + // ==================== 关联查询字段 ==================== + + /** + * 学生姓名 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学号 + */ + @Schema(description = "学号") + private String studentNumber; + + /** + * 班级名称 + */ + @Schema(description = "班级名称") + private String className; + + /** + * 专业名称 + */ + @Schema(description = "专业名称") + private String majorName; + + /** + * 学院名称 + */ + @Schema(description = "学院名称") + private String collegeName; + + // ==================== 统一基础字段(强制性,避免MyBatis映射错误) ==================== + + /** + * 创建者 + */ + @Schema(description = "创建者") + private Long createBy; + + /** + * 创建时间 + */ + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 更新者 + */ + @Schema(description = "更新者") + private Long updateBy; + + /** + * 更新时间 + */ + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java index 7af7458d..58137544 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipDailyMapper.java @@ -61,4 +61,24 @@ public interface ZsjyInternshipDailyMapper extends BaseMapper selectStudentMonthlyStatistics(@Param("studentId") Long studentId, + @Param("year") Integer year, + @Param("month") Integer month); + + /** + * 统计班主任管理的班级数量 + * + * @param teacherId 班主任ID + * @return 班级数量 + */ + Long countClassesByTeacherId(@Param("teacherId") Long teacherId); + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipVisitMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipVisitMapper.java new file mode 100644 index 00000000..1475b578 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipVisitMapper.java @@ -0,0 +1,40 @@ +package org.dromara.zsjy.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; + +import java.util.List; + +/** + * 班主任走访Mapper接口 + * + * @author system + * @date 2025-01-03 + */ +public interface ZsjyInternshipVisitMapper extends BaseMapperPlus { + + /** + * 根据ID查询班主任走访详情(包含关联学生信息) + * @param visitId 走访记录ID + * @return 走访详情VO + */ + ZsjyInternshipVisitVo selectById(Long visitId); + + /** + * 查询班主任走访列表(包含关联学生信息) + * @param bo 查询条件 + * @return 走访记录列表 + */ + List queryList(ZsjyInternshipVisitQueryBo bo); + + /** + * 分页查询班主任走访列表(包含关联学生信息) + * @param page 分页参数 + * @param bo 查询条件 + * @return 走访记录分页结果 + */ + Page queryPageList(Page page, ZsjyInternshipVisitQueryBo bo); +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java index a5d0dbed..fd52d24b 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipDailyService.java @@ -61,4 +61,14 @@ public interface IZsjyInternshipDailyService { */ R> getStatistics(ZsjyInternshipDailyQueryBo queryBo); + /** + * 获取学生个人月度考勤统计 + * + * @param studentId 学生ID + * @param year 年份 + * @param month 月份 + * @return 学生月度统计数据 + */ + R> getStudentMonthlyStatistics(Long studentId, Integer year, Integer month); + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipVisitService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipVisitService.java new file mode 100644 index 00000000..f2d624aa --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipVisitService.java @@ -0,0 +1,54 @@ +package org.dromara.zsjy.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; + +import java.util.Collection; +import java.util.List; + +/** + * 班主任走访Service接口 + * + * @author system + * @date 2025-01-03 + */ +public interface IZsjyInternshipVisitService { + + /** + * 查询班主任走访 + */ + ZsjyInternshipVisitVo queryById(Long visitId); + + /** + * 查询班主任走访列表 + */ + TableDataInfo queryPageList(ZsjyInternshipVisitQueryBo bo, PageQuery pageQuery); + + /** + * 查询班主任走访列表 + */ + List queryList(ZsjyInternshipVisitQueryBo bo); + + /** + * 新增班主任走访 + */ + Boolean insertByBo(ZsjyInternshipVisitBo bo); + + /** + * 修改班主任走访 + */ + Boolean updateByBo(ZsjyInternshipVisitBo bo); + + /** + * 校验并批量删除班主任走访信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 获取学生选项列表(用于下拉选择) + */ + List getStudentOptions(); +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java index 724291dd..817b237c 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipDailyServiceImpl.java @@ -48,8 +48,19 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi */ @Override public TableDataInfo queryPageList(ZsjyInternshipDailyQueryBo queryBo, PageQuery pageQuery) { + log.info("=== 实习日常查询后端调试日志 ==="); + log.info("1. 查询参数: {}", queryBo); + log.info("2. 分页参数: pageNum={}, pageSize={}", pageQuery.getPageNum(), pageQuery.getPageSize()); + + // 设置数据权限 + setDataPermission(queryBo); + log.info("3. 设置数据权限后的查询参数: {}", queryBo); + log.info("3.1. 权限控制参数: headTeacherId={}", queryBo.getHeadTeacherId()); + Page page = pageQuery.build(); List list = baseMapper.selectDailyList(page, queryBo); + log.info("4. 查询到的数据条数: {}", list != null ? list.size() : "null"); + return TableDataInfo.build(list); } @@ -58,6 +69,9 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi */ @Override public List queryList(ZsjyInternshipDailyQueryBo queryBo) { + // 设置数据权限 + setDataPermission(queryBo); + return baseMapper.selectDailyList(null, queryBo); } @@ -202,4 +216,94 @@ public class ZsjyInternshipDailyServiceImpl implements IZsjyInternshipDailyServi } } + /** + * 获取学生个人月度考勤统计 + */ + @Override + public R> getStudentMonthlyStatistics(Long studentId, Integer year, Integer month) { + try { + // 1. 参数校验 + if (ObjectUtil.isNull(studentId)) { + return R.fail("学生ID不能为空"); + } + if (ObjectUtil.isNull(year) || ObjectUtil.isNull(month)) { + return R.fail("年月参数不能为空"); + } + if (year < 2020 || year > 2030) { + return R.fail("年份参数不合法"); + } + if (month < 1 || month > 12) { + return R.fail("月份参数不合法"); + } + + // 2. 查询学生月度统计数据 + Map statistics = baseMapper.selectStudentMonthlyStatistics(studentId, year, month); + + // 3. 补充默认值 + if (ObjectUtil.isNull(statistics)) { + statistics = new HashMap<>(); + statistics.put("totalDays", 0); + statistics.put("checkedDays", 0); + statistics.put("missedDays", 0); + statistics.put("exceptionDays", 0); + } + + // 4. 确保数据类型正确 + statistics.put("totalDays", ObjectUtil.defaultIfNull(statistics.get("totalDays"), 0)); + statistics.put("checkedDays", ObjectUtil.defaultIfNull(statistics.get("checkedDays"), 0)); + statistics.put("missedDays", ObjectUtil.defaultIfNull(statistics.get("missedDays"), 0)); + statistics.put("exceptionDays", ObjectUtil.defaultIfNull(statistics.get("exceptionDays"), 0)); + + log.info("学生{}的{}-{}月度统计: {}", studentId, year, month, statistics); + return R.ok(statistics); + + } catch (Exception e) { + log.error("获取学生月度统计数据失败: studentId={}, year={}, month={}, error={}", + studentId, year, month, e.getMessage(), e); + return R.fail("获取学生月度统计数据失败: " + e.getMessage()); + } + } + + /** + * 设置数据权限(班主任只能查看自己班级的学生数据) + */ + private void setDataPermission(ZsjyInternshipDailyQueryBo queryBo) { + try { + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + log.info("=== 权限控制调试 ==="); + log.info("当前登录用户ID: {}", currentUserId); + + if (ObjectUtil.isNotNull(currentUserId)) { + // 检查该用户是否是班主任 + Long classTeacherCount = baseMapper.countClassesByTeacherId(currentUserId); + log.info("用户ID {}作为班主任管理的班级数量: {}", currentUserId, classTeacherCount); + + if (classTeacherCount > 0) { + // 是班主任,设置权限限制 + queryBo.setHeadTeacherId(currentUserId); + log.info("✅ 权限判断:用户是班主任,设置headTeacherId={}", currentUserId); + } else { + // 不是班主任,设置不存在的班主任ID,使其无法查看任何数据 + log.info("❌ 权限判断:用户不是班主任,设置权限限制"); + + // 设置一个不存在的班主任ID(-1),这样SQL查询结果为空 + queryBo.setHeadTeacherId(-1L); + log.info("🔒 设置数据权限:非班主任用户无法查看数据,headTeacherId=-1"); + } + + // 输出最终的权限设置 + log.info("🎯 最终权限设置:queryBo.headTeacherId = {}", queryBo.getHeadTeacherId()); + } else { + log.warn("⚠️ 未获取到当前用户ID,设置权限限制"); + queryBo.setHeadTeacherId(-1L); + } + } catch (Exception e) { + log.error("❌ 设置数据权限失败:{}", e.getMessage(), e); + // 异常情况下,设置权限限制以确保数据安全 + queryBo.setHeadTeacherId(-1L); + log.warn("🔒 异常情况下设置权限限制,headTeacherId=-1"); + } + } + } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipVisitServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipVisitServiceImpl.java new file mode 100644 index 00000000..40fbfc66 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipVisitServiceImpl.java @@ -0,0 +1,116 @@ +package org.dromara.zsjy.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.ZsjyInternshipVisit; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipVisitQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipVisitVo; +import org.dromara.zsjy.mapper.ZsjyInternshipVisitMapper; +import org.dromara.zsjy.service.IZsjyInternshipVisitService; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; + +/** + * 班主任走访Service业务层处理 + * + * @author system + * @date 2025-01-03 + */ +@RequiredArgsConstructor +@Service +public class ZsjyInternshipVisitServiceImpl implements IZsjyInternshipVisitService { + + private final ZsjyInternshipVisitMapper baseMapper; + + /** + * 查询班主任走访 + */ + @Override + public ZsjyInternshipVisitVo queryById(Long visitId){ + return baseMapper.selectById(visitId); + } + + /** + * 查询班主任走访列表 + */ + @Override + public TableDataInfo queryPageList(ZsjyInternshipVisitQueryBo bo, PageQuery pageQuery) { + // 使用自定义分页查询方法获取包含学生信息的完整数据 + Page result = baseMapper.queryPageList(pageQuery.build(), bo); + return TableDataInfo.build(result); + } + + /** + * 查询班主任走访列表 + */ + @Override + public List queryList(ZsjyInternshipVisitQueryBo bo) { + // 使用自定义查询方法获取包含学生信息的完整数据 + return baseMapper.queryList(bo); + } + + /** + * 新增班主任走访 + */ + @Override + public Boolean insertByBo(ZsjyInternshipVisitBo bo) { + ZsjyInternshipVisit add = MapstructUtils.convert(bo, ZsjyInternshipVisit.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setVisitId(add.getVisitId()); + } + return flag; + } + + /** + * 修改班主任走访 + */ + @Override + public Boolean updateByBo(ZsjyInternshipVisitBo bo) { + ZsjyInternshipVisit update = MapstructUtils.convert(bo, ZsjyInternshipVisit.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(ZsjyInternshipVisit entity){ + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除班主任走访 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + /** + * 获取学生选项列表(用于下拉选择) + */ + @Override + public List getStudentOptions() { + // TODO 实现获取有实习信息的学生列表 + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.select(ZsjyInternshipVisit::getStudentId, ZsjyInternshipVisit::getInternshipId); + lqw.groupBy(ZsjyInternshipVisit::getStudentId); + return baseMapper.selectVoList(lqw); + } +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml index e1904a0e..6e1e5012 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipDailyMapper.xml @@ -104,6 +104,15 @@ AND i.company_name LIKE CONCAT('%', #{queryBo.companyName}, '%') + + + AND EXISTS ( + SELECT 1 FROM xg_student_msg s + WHERE s.id = d.student_id + AND s.class_teacher_id = #{queryBo.headTeacherId} + AND s.deleted = 0 + ) + ORDER BY d.check_date DESC, d.create_time DESC @@ -175,6 +184,44 @@ ) as attendanceRate + + + + + + diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipVisitMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipVisitMapper.xml new file mode 100644 index 00000000..2e2e4270 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipVisitMapper.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + v.visit_id, + v.tenant_id, + v.internship_id, + v.student_id, + v.company_name, + v.company_address, + v.visit_date, + v.visitor_name, + v.visitor_position, + v.visit_purpose, + v.company_situation, + v.student_performance, + v.problems_found, + v.suggestions, + v.visit_summary, + v.visit_images, + v.visit_files, + v.status, + v.remark, + v.create_by, + v.create_time, + v.update_by, + v.update_time, + -- 关联查询学生信息 + s.name as student_name, + s.student_no as student_number, + s.class_name as class_name, + s.major_name as major_name, + s.college_name as college_name + from zsjy_internship_visit v + left join zsjy_internship_info ii on v.internship_id = ii.internship_id and ii.tenant_id = '000000' + left join xg_student_msg s on v.student_id = s.id and s.tenant_id = '000000' and s.deleted = 0 + + + + + + + + + diff --git a/mobile/src/api/internship.js b/mobile/src/api/internship.js index 53a12c31..ad4b77dc 100644 --- a/mobile/src/api/internship.js +++ b/mobile/src/api/internship.js @@ -63,7 +63,7 @@ export const getCalendarData = (studentId, year, month) => { */ export const getStatistics = (params) => { return request({ - url: '/zsjy/internship/daily/statistics', + url: '/zsjy/internship/daily/monthly-statistics', method: 'GET', params }); diff --git a/mobile/src/pages/internship/index.vue b/mobile/src/pages/internship/index.vue index ca7c5b41..13d93243 100644 --- a/mobile/src/pages/internship/index.vue +++ b/mobile/src/pages/internship/index.vue @@ -90,75 +90,121 @@ - +