diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java index f1678340..e0638e8b 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/InternshipInfoController.java @@ -157,7 +157,7 @@ public class InternshipInfoController extends BaseController { } /** - * 修改实习信息 + * 33 */ @Operation(summary = "修改实习信息", description = "修改学生实习信息") @SaCheckPermission("zsjy:internship:edit") @@ -202,4 +202,27 @@ public class InternshipInfoController extends BaseController { return R.fail("删除实习信息失败:" + e.getMessage()); } } + + /** + * 获取当前学生的实习信息 + */ + @Operation(summary = "获取当前学生实习信息", description = "获取当前登录学生的实习信息,用于月度总结等功能") + @SaCheckPermission("zsjy:internship:query") + @GetMapping("/current") + public R getCurrentStudentInternship() { + try { + log.info("=== 获取当前学生实习信息接口被调用 ==="); + InternshipInfoVO result = internshipInfoService.getCurrentStudentInternship(); + if (result != null) { + log.info("成功获取当前学生实习信息,实习ID:{}", result.getInternshipId()); + return R.ok(result); + } else { + log.info("当前学生没有实习信息"); + return R.ok(); // 返回空数据,前端需要处理这种情况 + } + } catch (Exception e) { + log.error("获取当前学生实习信息失败:{}", e.getMessage(), e); + return R.fail("获取实习信息失败:" + e.getMessage()); + } + } } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipMonthlyController.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipMonthlyController.java new file mode 100644 index 00000000..996a1112 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/controller/ZsjyInternshipMonthlyController.java @@ -0,0 +1,238 @@ +package org.dromara.zsjy.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +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.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 jakarta.servlet.http.HttpServletResponse; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; +import org.dromara.zsjy.service.IZsjyInternshipMonthlyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 学生月度实习总结Controller + * + * @author system + * @date 2025-01-03 + */ +@Slf4j +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/zsjy/internship/monthly") +@Tag(name = "学生月度实习总结", description = "学生月度实习总结填写管理") +public class ZsjyInternshipMonthlyController extends BaseController { + + private final IZsjyInternshipMonthlyService monthlyService; + + /** + * 查询学生月度实习总结列表 + */ + @Operation(summary = "查询学生月度实习总结列表", description = "学生查看自己的月度总结列表") + @SaCheckPermission("zsjy:internshipmonthly:list") + @GetMapping("/list") + public TableDataInfo list(@Validated ZsjyInternshipMonthlyQueryBo queryBo, PageQuery pageQuery) { + log.info("=== Controller层调试 ==="); + log.info("接口被调用: /zsjy/internship/monthly/list"); + log.info("查询参数: {}", queryBo); + log.info("分页参数: {}", pageQuery); + + return monthlyService.queryPageList(queryBo, pageQuery); + } + + /** + * 查询学生月度实习总结详细信息 + */ + @Operation(summary = "查询学生月度实习总结详细信息", description = "根据ID查询月度总结详情") + @SaCheckPermission("zsjy:internshipmonthly:query") + @GetMapping("/{monthlyId}") + public R getInfo(@Parameter(description = "月度总结ID") + @PathVariable Long monthlyId) { + log.info("查询月度总结详情, ID: {}", monthlyId); + return R.ok(monthlyService.queryById(monthlyId)); + } + + /** + * 新增学生月度实习总结 + */ + @Operation(summary = "新增学生月度实习总结", description = "学生创建新的月度总结") + @SaCheckPermission("zsjy:internshipmonthly:add") + @PostMapping + public R add(@Validated(AddGroup.class) @RequestBody ZsjyInternshipMonthlyAddBo addBo) { + log.info("新增月度总结: {}", addBo); + + if (!monthlyService.insertByBo(addBo)) { + return R.fail("新增月度总结失败"); + } + return R.ok(); + } + + /** + * 修改学生月度实习总结 + */ + @Operation(summary = "修改学生月度实习总结", description = "学生编辑自己的月度总结") + @SaCheckPermission("zsjy:internshipmonthly:edit") + @PutMapping + public R edit(@Validated(EditGroup.class) @RequestBody ZsjyInternshipMonthlyAddBo addBo) { + log.info("修改月度总结: {}", addBo); + + if (!monthlyService.updateByBo(addBo)) { + return R.fail("修改月度总结失败"); + } + return R.ok(); + } + + /** + * 删除学生月度实习总结 + */ + @Operation(summary = "删除学生月度实习总结", description = "学生删除自己的草稿状态月度总结") + @SaCheckPermission("zsjy:internshipmonthly:remove") + @DeleteMapping("/{monthlyIds}") + public R remove(@Parameter(description = "月度总结ID串") + @PathVariable Long[] monthlyIds) { + log.info("删除月度总结, IDs: {}", (Object) monthlyIds); + + if (!monthlyService.deleteByIds(List.of(monthlyIds))) { + return R.fail("删除月度总结失败"); + } + return R.ok(); + } + + /** + * 提交月度总结审批 + */ + @Operation(summary = "提交月度总结审批", description = "学生提交月度总结给班主任审批") + @SaCheckPermission("zsjy:internshipmonthly:submit") + @PutMapping("/submit/{monthlyId}") + public R submit(@Parameter(description = "月度总结ID") + @PathVariable Long monthlyId) { + log.info("提交月度总结审批, ID: {}", monthlyId); + + if (!monthlyService.submitForApproval(monthlyId)) { + return R.fail("提交月度总结失败"); + } + return R.ok(); + } + + /** + * 自动保存草稿 + */ + @Operation(summary = "自动保存草稿", description = "学生编辑时自动保存草稿") + @SaCheckPermission("zsjy:internshipmonthly:autosave") + @PostMapping("/autosave") + public R autoSave(@RequestBody ZsjyInternshipMonthlyAddBo addBo) { + log.info("自动保存草稿: {}", addBo); + + if (!monthlyService.autoSaveDraft(addBo)) { + return R.fail("自动保存失败"); + } + return R.ok(); + } + + /** + * 检查月份是否存在总结 + */ + @Operation(summary = "检查月份是否存在总结", description = "检查学生某月是否已有总结记录") + @SaCheckPermission("zsjy:internshipmonthly:check") + @GetMapping("/check/{summaryMonth}") + public R checkMonth(@Parameter(description = "总结月份") + @PathVariable String summaryMonth, + @Parameter(description = "排除的总结ID") + @RequestParam(required = false) Long monthlyId) { + log.info("检查月份总结, 月份: {}, 排除ID: {}", summaryMonth, monthlyId); + + Boolean exists = monthlyService.checkMonthExists(null, summaryMonth, monthlyId); + return R.ok(exists); + } + + /** + * 获取月度总结模板 + */ + @Operation(summary = "获取月度总结模板", description = "获取指定月份的总结模板信息") + @SaCheckPermission("zsjy:internshipmonthly:template") + @GetMapping("/template/{summaryMonth}") + public R getTemplate(@Parameter(description = "总结月份") + @PathVariable String summaryMonth) { + log.info("获取月度总结模板, 月份: {}", summaryMonth); + + ZsjyInternshipMonthlyVo template = monthlyService.getMonthlyTemplate(summaryMonth); + return R.ok(template); + } + + /** + * 查询学生指定实习的月度总结列表 + */ + @Operation(summary = "查询学生指定实习的月度总结列表", description = "查询学生某个实习的所有月度总结") + @SaCheckPermission("zsjy:internshipmonthly:list") + @GetMapping("/internship/{internshipId}") + public R> listByInternship(@Parameter(description = "实习ID") + @PathVariable Long internshipId) { + log.info("查询实习月度总结列表, 实习ID: {}", internshipId); + + List list = monthlyService.queryByStudentAndInternship(null, internshipId); + return R.ok(list); + } + + /** + * 统计学生各状态的总结数量 + */ + @Operation(summary = "统计学生各状态的总结数量", description = "统计学生草稿、待审核、已审核等状态的总结数量") + @SaCheckPermission("zsjy:internshipmonthly:statistics") + @GetMapping("/statistics") + public R> statistics() { + log.info("统计学生月度总结状态"); + + List statistics = monthlyService.countByStatus(null); + return R.ok(statistics); + } + + /** + * 获取学生当前实习信息 + */ + @Operation(summary = "获取学生当前实习信息", description = "获取当前登录学生的进行中实习信息") + @SaCheckPermission("zsjy:internshipmonthly:add") + @GetMapping("/current-internship") + public R> getCurrentInternship() { + log.info("获取学生当前实习信息"); + + Map internshipInfo = monthlyService.getCurrentInternshipInfo(); + if (internshipInfo == null || internshipInfo.isEmpty()) { + return R.fail("您当前没有进行中的实习记录"); + } + + return R.ok(internshipInfo); + } + + /** + * 导出学生月度实习总结 + */ + @Operation(summary = "导出学生月度实习总结", description = "导出学生的月度总结数据") + @SaCheckPermission("zsjy:internshipmonthly:export") + @Log(title = "学生月度实习总结", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(@RequestBody ZsjyInternshipMonthlyQueryBo queryBo, HttpServletResponse response) { + log.info("导出月度总结: {}", queryBo); + + List list = monthlyService.queryList(queryBo); + ExcelUtil.exportExcel(list, "学生月度实习总结", ZsjyInternshipMonthlyVo.class, response); + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java index ab08c3b3..523eafe1 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipInfo.java @@ -1,6 +1,7 @@ package org.dromara.zsjy.domain; import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @@ -159,5 +160,11 @@ public class ZsjyInternshipInfo extends BaseEntity { * 实习状态(1进行中 2已完成 3中断) */ private String internshipStatus; + + /** + * 删除标志(0代表存在 1代表删除) + */ + @TableLogic(value = "0", delval = "1") + private Long deleted; } \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipMonthly.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipMonthly.java new file mode 100644 index 00000000..4213a17a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/ZsjyInternshipMonthly.java @@ -0,0 +1,191 @@ +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.math.BigDecimal; +import java.util.Date; + +/** + * 学生月度实习总结实体对象 zsjy_internship_monthly + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("zsjy_internship_monthly") +public class ZsjyInternshipMonthly extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 月度总结ID (对应数据库monthly_id字段) + */ + @TableId(value = "monthly_id", type = IdType.ASSIGN_ID) + private Long id; + + /** + * 租户ID + */ + @TableField("tenant_id") + private String tenantId; + + /** + * 实习ID + */ + @TableField("internship_id") + private Long internshipId; + + /** + * 学生ID + */ + @TableField("student_id") + private Long studentId; + + /** + * 总结月份 (格式: 2025-01) + */ + @TableField("summary_month") + private String summaryMonth; + + /** + * 月份序号 (1-12) + */ + @TableField("month_number") + private Integer monthNumber; + + /** + * 工作内容 (必填,500字以内) + */ + @TableField("work_content") + private String workContent; + + /** + * 主要成果 + */ + @TableField("achievements") + private String achievements; + + /** + * 学习收获 + */ + @TableField("learning_gains") + private String learningGains; + + /** + * 遇到的问题 + */ + @TableField("problems_encountered") + private String problemsEncountered; + + /** + * 自我评价 + */ + @TableField("self_evaluation") + private String selfEvaluation; + + /** + * 下月工作计划 + */ + @TableField("next_month_plan") + private String nextMonthPlan; + + /** + * 专业技能自评 (1-10分) + */ + @TableField("professional_skills") + private Integer professionalSkills; + + /** + * 沟通技能自评 (1-10分) + */ + @TableField("communication_skills") + private Integer communicationSkills; + + /** + * 团队协作自评 (1-10分) - 修正字段映射 + */ + @TableField("teamwork_ability") + private Integer teamworkAbility; + + /** + * 学习能力自评 (1-10分) + */ + @TableField("learning_ability") + private Integer learningAbility; + + /** + * 工作态度自评 (1-10分) + */ + @TableField("work_attitude") + private Integer workAttitude; + + /** + * 审核状态 (0:待审核 1:已审核 2:审核拒绝 3:草稿) - 修正字段映射 + */ + @TableField("review_status") + private Integer reviewStatus; + + /** + * 审核人ID - 修正字段映射 + */ + @TableField("review_by") + private Long reviewBy; + + /** + * 审核时间 + */ + @TableField("review_time") + private Date reviewTime; + + /** + * 审核评分 (百分制) + */ + @TableField("review_score") + private BigDecimal reviewScore; + + /** + * 审核意见 - 修正字段映射 + */ + @TableField("review_comment") + private String reviewComment; + + /** + * 提交时间 + */ + @TableField("submit_time") + private Date submitTime; + + /** + * 字数统计 + */ + @TableField("word_count") + private Integer wordCount; + + /** + * 是否逾期 (0:未逾期 1:已逾期) + */ + @TableField("is_overdue") + private Integer isOverdue; + + /** + * 提交截止日期 - 修正字段映射 + */ + @TableField("submit_deadline") + private Date submitDeadline; + + /** + * 附件信息 (JSON格式存储) + */ + @TableField("attachments") + private String attachments; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyAddBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyAddBo.java new file mode 100644 index 00000000..9cfd7357 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyAddBo.java @@ -0,0 +1,147 @@ +package org.dromara.zsjy.domain.bo; + +import io.swagger.v3.oas.annotations.media.Schema; +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.util.Date; + +/** + * 学生月度实习总结新增/编辑对象 ZsjyInternshipMonthlyAddBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "学生月度实习总结新增/编辑对象") +public class ZsjyInternshipMonthlyAddBo extends BaseEntity { + + /** + * 月度总结ID + */ + @Schema(description = "月度总结ID") + @NotNull(message = "月度总结ID不能为空", groups = { EditGroup.class }) + private Long monthlyId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + @NotNull(message = "实习ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long internshipId; + + /** + * 总结月份 (格式: 2025-01) + */ + @Schema(description = "总结月份") + @NotBlank(message = "总结月份不能为空", groups = { AddGroup.class, EditGroup.class }) + @Pattern(regexp = "^\\d{4}-\\d{2}$", message = "月份格式应为 YYYY-MM", groups = { AddGroup.class, EditGroup.class }) + private String summaryMonth; + + /** + * 工作内容 (必填,500字以内) + */ + @Schema(description = "工作内容") + @NotBlank(message = "工作内容不能为空", groups = { AddGroup.class, EditGroup.class }) + @Size(max = 500, message = "工作内容不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String workContent; + + /** + * 主要成果 + */ + @Schema(description = "主要成果") + @Size(max = 500, message = "主要成果不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String achievements; + + /** + * 学习收获 + */ + @Schema(description = "学习收获") + @Size(max = 500, message = "学习收获不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String learningGains; + + /** + * 遇到的问题(包含解决方案,按2-A方案合并) + */ + @Schema(description = "遇到的问题") + @Size(max = 1000, message = "遇到的问题不能超过1000字", groups = { AddGroup.class, EditGroup.class }) + private String problemsEncountered; + + /** + * 自我评价 + */ + @Schema(description = "自我评价") + @Size(max = 500, message = "自我评价不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String selfEvaluation; + + /** + * 下月工作计划 + */ + @Schema(description = "下月工作计划") + @Size(max = 500, message = "下月工作计划不能超过500字", groups = { AddGroup.class, EditGroup.class }) + private String nextMonthPlan; + + /** + * 专业技能自评 (1-10分) + */ + @Schema(description = "专业技能自评") + @Min(value = 1, message = "专业技能自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "专业技能自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer professionalSkills; + + /** + * 沟通技能自评 (1-10分) + */ + @Schema(description = "沟通技能自评") + @Min(value = 1, message = "沟通技能自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "沟通技能自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer communicationSkills; + + /** + * 团队协作自评 (1-10分) - 修正字段映射 + */ + @Schema(description = "团队协作自评") + @Min(value = 1, message = "团队协作自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "团队协作自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer teamworkAbility; + + /** + * 学习能力自评 (1-10分) + */ + @Schema(description = "学习能力自评") + @Min(value = 1, message = "学习能力自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "学习能力自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer learningAbility; + + /** + * 工作态度自评 (1-10分) + */ + @Schema(description = "工作态度自评") + @Min(value = 1, message = "工作态度自评最小值为1", groups = { AddGroup.class, EditGroup.class }) + @Max(value = 10, message = "工作态度自评最大值为10", groups = { AddGroup.class, EditGroup.class }) + private Integer workAttitude; + + /** + * 附件信息 (JSON格式存储) + */ + @Schema(description = "附件信息") + private String attachments; + + /** + * 审核状态 (0-待审核 1-已审核 2-审核拒绝 3-草稿) + */ + @Schema(description = "审核状态") + private Integer reviewStatus; + + /** + * 提交时间 + */ + @Schema(description = "提交时间") + private Date submitTime; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyQueryBo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyQueryBo.java new file mode 100644 index 00000000..8387f8ac --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/bo/ZsjyInternshipMonthlyQueryBo.java @@ -0,0 +1,61 @@ +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; + +/** + * 学生月度实习总结查询对象 ZsjyInternshipMonthlyQueryBo + * + * @author system + * @date 2025-01-03 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema(description = "学生月度实习总结查询对象") +public class ZsjyInternshipMonthlyQueryBo extends BaseEntity { + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 总结月份 (格式: 2025-01) + */ + @Schema(description = "总结月份") + private String summaryMonth; + + /** + * 审核状态 (0:待审核 1:已审核 2:审核拒绝 3:草稿) + */ + @Schema(description = "审核状态") + private Integer status; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 是否逾期 (0:未逾期 1:已逾期) + */ + @Schema(description = "是否逾期") + private Integer isOverdue; + + /** + * 开始月份 + */ + @Schema(description = "开始月份") + private String startMonth; + + /** + * 结束月份 + */ + @Schema(description = "结束月份") + private String endMonth; + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipMonthlyVo.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipMonthlyVo.java new file mode 100644 index 00000000..d614ceb8 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/domain/vo/ZsjyInternshipMonthlyVo.java @@ -0,0 +1,253 @@ +package org.dromara.zsjy.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 学生月度实习总结视图对象 ZsjyInternshipMonthlyVo + * + * @author system + * @date 2025-01-03 + */ +@Data +@Schema(description = "学生月度实习总结视图对象") +public class ZsjyInternshipMonthlyVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 月度总结ID + */ + @Schema(description = "月度总结ID") + private Long monthlyId; + + /** + * 实习ID + */ + @Schema(description = "实习ID") + private Long internshipId; + + /** + * 学生ID + */ + @Schema(description = "学生ID") + private Long studentId; + + /** + * 学生姓名 - 通过关联查询获取 + */ + @Schema(description = "学生姓名") + private String studentName; + + /** + * 学生学号 - 通过关联查询获取 + */ + @Schema(description = "学生学号") + private String studentNumber; + + /** + * 总结月份 (格式: 2025-01) + */ + @Schema(description = "总结月份") + private String summaryMonth; + + /** + * 月份序号 (1-12) + */ + @Schema(description = "月份序号") + private Integer monthNumber; + + /** + * 工作内容 + */ + @Schema(description = "工作内容") + private String workContent; + + /** + * 主要成果 + */ + @Schema(description = "主要成果") + private String achievements; + + /** + * 学习收获 + */ + @Schema(description = "学习收获") + private String learningGains; + + /** + * 遇到的问题(包含解决方案,按2-A方案合并) + */ + @Schema(description = "遇到的问题") + private String problemsEncountered; + + /** + * 自我评价 + */ + @Schema(description = "自我评价") + private String selfEvaluation; + + /** + * 下月工作计划 + */ + @Schema(description = "下月工作计划") + private String nextMonthPlan; + + /** + * 专业技能自评 (1-10分) + */ + @Schema(description = "专业技能自评") + private Integer professionalSkills; + + /** + * 沟通技能自评 (1-10分) + */ + @Schema(description = "沟通技能自评") + private Integer communicationSkills; + + /** + * 团队协作自评 (1-10分) - 修正字段映射 + */ + @Schema(description = "团队协作自评") + private Integer teamworkAbility; + + /** + * 学习能力自评 (1-10分) + */ + @Schema(description = "学习能力自评") + private Integer learningAbility; + + /** + * 工作态度自评 (1-10分) + */ + @Schema(description = "工作态度自评") + private Integer workAttitude; + + /** + * 审核状态 (0:待审核 1:已审核 2:审核拒绝 3:草稿) - 修正字段映射 + */ + @Schema(description = "审核状态") + private Integer reviewStatus; + + /** + * 状态文本描述 + */ + @Schema(description = "状态文本描述") + private String statusText; + + /** + * 审核人ID - 修正字段映射 + */ + @Schema(description = "审核人ID") + private Long reviewBy; + + /** + * 审核人姓名 - 通过关联查询获取 + */ + @Schema(description = "审核人姓名") + private String reviewerName; + + /** + * 审核时间 + */ + @Schema(description = "审核时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date reviewTime; + + /** + * 审核评分 (百分制) + */ + @Schema(description = "审核评分") + private BigDecimal reviewScore; + + /** + * 审核意见 - 修正字段映射 + */ + @Schema(description = "审核意见") + private String reviewComment; + + /** + * 提交时间 + */ + @Schema(description = "提交时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date submitTime; + + /** + * 字数统计 + */ + @Schema(description = "字数统计") + private Integer wordCount; + + /** + * 是否逾期 (0:未逾期 1:已逾期) + */ + @Schema(description = "是否逾期") + private Integer isOverdue; + + /** + * 提交截止日期 - 修正字段映射 + */ + @Schema(description = "提交截止日期") + @JsonFormat(pattern = "yyyy-MM-dd") + private Date submitDeadline; + + /** + * 附件信息 + */ + @Schema(description = "附件信息") + private String attachments; + + /** + * 是否可编辑 (草稿状态才可编辑) + */ + @Schema(description = "是否可编辑") + private Boolean editable; + + /** + * 是否可删除 (草稿状态才可删除) + */ + @Schema(description = "是否可删除") + private Boolean deletable; + + /** + * 是否可提交 (草稿状态才可提交) + */ + @Schema(description = "是否可提交") + private Boolean submittable; + + // VO类基础字段规范 - 必须包含的四个字段 + /** + * 创建者 + */ + @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/ZsjyInternshipMonthlyMapper.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipMonthlyMapper.java new file mode 100644 index 00000000..21069aef --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/mapper/ZsjyInternshipMonthlyMapper.java @@ -0,0 +1,82 @@ +package org.dromara.zsjy.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.dromara.zsjy.domain.ZsjyInternshipMonthly; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; + +import java.util.List; + +/** + * 学生月度实习总结Mapper接口 + * + * @author system + * @date 2025-01-03 + */ +public interface ZsjyInternshipMonthlyMapper extends BaseMapper { + + /** + * 查询学生月度实习总结 + * + * @param monthlyId 月度总结ID + * @return 学生月度实习总结 + */ + ZsjyInternshipMonthlyVo selectVoById(Long monthlyId); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @return 学生月度实习总结集合 + */ + Page selectVoPage(Page page, @Param("bo") ZsjyInternshipMonthlyQueryBo queryBo); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @return 学生月度实习总结集合 + */ + List selectVoList(@Param("bo") ZsjyInternshipMonthlyQueryBo queryBo); + + /** + * 检查学生某月是否已有总结 + * + * @param studentId 学生ID + * @param summaryMonth 总结月份 + * @param monthlyId 排除的总结ID(用于编辑时检查) + * @return 是否存在 + */ + boolean existsByStudentAndMonth(@Param("studentId") Long studentId, + @Param("summaryMonth") String summaryMonth, + @Param("monthlyId") Long monthlyId); + + /** + * 批量删除学生月度实习总结 + * + * @param monthlyIds 需要删除的数据ID + * @return 影响行数 + */ + int deleteByIds(@Param("monthlyIds") Long[] monthlyIds); + + /** + * 查询学生指定实习的月度总结列表 + * + * @param studentId 学生ID + * @param internshipId 实习ID + * @return 月度总结列表 + */ + List selectByStudentAndInternship(@Param("studentId") Long studentId, + @Param("internshipId") Long internshipId); + + /** + * 统计学生各状态的总结数量 + * + * @param studentId 学生ID + * @return 状态统计结果 + */ + List countByStatus(@Param("studentId") Long studentId); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java index 15065e21..660703d5 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IInternshipInfoService.java @@ -98,4 +98,11 @@ public interface IInternshipInfoService { * @return 是否成功 */ Boolean deleteInternshipInfo(Long[] internshipIds); + + /** + * 获取当前学生的实习信息 + * + * @return 当前学生的实习信息,如果没有则返回null + */ + InternshipInfoVO getCurrentStudentInternship(); } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipMonthlyService.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipMonthlyService.java new file mode 100644 index 00000000..2e167002 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/IZsjyInternshipMonthlyService.java @@ -0,0 +1,137 @@ +package org.dromara.zsjy.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 学生月度实习总结Service接口 + * + * @author system + * @date 2025-01-03 + */ +public interface IZsjyInternshipMonthlyService { + + /** + * 查询学生月度实习总结 + * + * @param monthlyId 月度总结ID + * @return 学生月度实习总结 + */ + ZsjyInternshipMonthlyVo queryById(Long monthlyId); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @param pageQuery 分页参数 + * @return 学生月度实习总结分页结果 + */ + TableDataInfo queryPageList(ZsjyInternshipMonthlyQueryBo queryBo, PageQuery pageQuery); + + /** + * 查询学生月度实习总结列表 + * + * @param queryBo 查询条件 + * @return 学生月度实习总结集合 + */ + List queryList(ZsjyInternshipMonthlyQueryBo queryBo); + + /** + * 新增学生月度实习总结 + * + * @param addBo 学生月度实习总结 + * @return 新增结果 + */ + Boolean insertByBo(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 修改学生月度实习总结 + * + * @param addBo 学生月度实习总结 + * @return 修改结果 + */ + Boolean updateByBo(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 校验并批量删除学生月度实习总结信息 + * + * @param ids 需要删除的学生月度实习总结ID + * @return 删除结果 + */ + Boolean deleteByIds(Collection ids); + + /** + * 提交月度总结审批 + * + * @param monthlyId 月度总结ID + * @return 提交结果 + */ + Boolean submitForApproval(Long monthlyId); + + /** + * 自动保存草稿 + * + * @param addBo 学生月度实习总结 + * @return 保存结果 + */ + Boolean autoSaveDraft(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 检查学生某月是否已有总结 + * + * @param studentId 学生ID + * @param summaryMonth 总结月份 + * @param monthlyId 排除的总结ID(用于编辑时检查) + * @return 是否存在 + */ + Boolean checkMonthExists(Long studentId, String summaryMonth, Long monthlyId); + + /** + * 获取月度总结模板信息 + * + * @param summaryMonth 总结月份 + * @return 模板信息 + */ + ZsjyInternshipMonthlyVo getMonthlyTemplate(String summaryMonth); + + /** + * 查询学生指定实习的月度总结列表 + * + * @param studentId 学生ID + * @param internshipId 实习ID + * @return 月度总结列表 + */ + List queryByStudentAndInternship(Long studentId, Long internshipId); + + /** + * 统计学生各状态的总结数量 + * + * @param studentId 学生ID + * @return 状态统计结果 + */ + List countByStatus(Long studentId); + + /** + * 计算总结内容字数 + * + * @param addBo 总结内容 + * @return 字数统计 + */ + Integer calculateWordCount(ZsjyInternshipMonthlyAddBo addBo); + + /** + * 获取学生当前实习信息 + * + * @return 当前实习信息 + */ + Map getCurrentInternshipInfo(); + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java index 8f33c0c8..eba50d51 100644 --- a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/InternshipInfoServiceImpl.java @@ -459,4 +459,53 @@ public class InternshipInfoServiceImpl implements IInternshipInfoService { } return studentInfo; } + + /** + * 获取当前学生的实习信息 + * + * @return 当前学生的实习信息,如果没有则返回null + */ + @Override + public InternshipInfoVO getCurrentStudentInternship() { + log.info("开始获取当前学生的实习信息"); + + // 获取当前登录用户ID + Long currentUserId = LoginHelper.getUserId(); + log.info("当前登录用户ID:{}", currentUserId); + + if (currentUserId == null) { + log.error("当前用户未登录"); + throw new ServiceException("用户未登录"); + } + + // 根据用户ID查询实习信息 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ZsjyInternshipInfo::getStudentId, currentUserId) + .eq(ZsjyInternshipInfo::getDeleted, 0) + .orderByDesc(ZsjyInternshipInfo::getCreateTime) + .last("LIMIT 1"); // 只取最新的一条记录 + + ZsjyInternshipInfo internshipInfo = zsjyInternshipInfoMapper.selectOne(wrapper); + log.info("查询到的实习信息:{}", internshipInfo != null ? internshipInfo.getInternshipId() : "无"); + + if (internshipInfo == null) { + log.info("当前学生没有实习信息"); + return null; + } + + // 转换为VO + InternshipInfoVO vo = BeanUtil.toBean(internshipInfo, InternshipInfoVO.class); + + // 查询学生信息并设置到VO中 + XgStudentMsgVo studentInfo = getStudentInfo(currentUserId); + vo.setStudentName(studentInfo.getName()); + vo.setStudentNo(studentInfo.getStudentNo()); + vo.setClassName(studentInfo.getClassName()); + vo.setMajorName(studentInfo.getMajorName()); + vo.setCollegeName(studentInfo.getCollegeName()); + vo.setGrade(studentInfo.getGrade()); + + log.info("成功获取当前学生实习信息,实习ID:{}", vo.getInternshipId()); + return vo; + } } diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipMonthlyServiceImpl.java b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipMonthlyServiceImpl.java new file mode 100644 index 00000000..0ebbbada --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/java/org/dromara/zsjy/service/impl/ZsjyInternshipMonthlyServiceImpl.java @@ -0,0 +1,465 @@ +package org.dromara.zsjy.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +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.common.satoken.utils.LoginHelper; +import org.dromara.zsjy.domain.ZsjyInternshipMonthly; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyAddBo; +import org.dromara.zsjy.domain.bo.ZsjyInternshipMonthlyQueryBo; +import org.dromara.zsjy.domain.vo.ZsjyInternshipMonthlyVo; +import org.dromara.zsjy.mapper.ZsjyInternshipMonthlyMapper; +import org.dromara.zsjy.mapper.ZsjyInternshipInfoMapper; +import org.dromara.zsjy.domain.ZsjyInternshipInfo; +import org.dromara.zsjy.service.IZsjyInternshipMonthlyService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.YearMonth; +import java.util.*; +import java.time.ZoneId; + +/** + * 学生月度实习总结Service业务层处理 + * + * @author system + * @date 2025-01-03 + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class ZsjyInternshipMonthlyServiceImpl implements IZsjyInternshipMonthlyService { + + private final ZsjyInternshipMonthlyMapper baseMapper; + private final ZsjyInternshipInfoMapper internshipInfoMapper; + + /** + * 查询学生月度实习总结 + */ + @Override + public ZsjyInternshipMonthlyVo queryById(Long monthlyId) { + ZsjyInternshipMonthlyVo vo = baseMapper.selectVoById(monthlyId); + if (ObjectUtil.isNull(vo)) { + throw new ServiceException("月度总结不存在"); + } + return vo; + } + + /** + * 查询学生月度实习总结分页列表 + */ + @Override + public TableDataInfo queryPageList(ZsjyInternshipMonthlyQueryBo queryBo, PageQuery pageQuery) { + // 如果没有指定学生ID,则使用当前登录用户ID(学生端) + if (ObjectUtil.isNull(queryBo.getStudentId())) { + queryBo.setStudentId(LoginHelper.getUserId()); + } + + Page result = baseMapper.selectVoPage(pageQuery.build(), queryBo); + return TableDataInfo.build(result); + } + + /** + * 查询学生月度实习总结列表 + */ + @Override + public List queryList(ZsjyInternshipMonthlyQueryBo queryBo) { + // 如果没有指定学生ID,则使用当前登录用户ID(学生端) + if (ObjectUtil.isNull(queryBo.getStudentId())) { + queryBo.setStudentId(LoginHelper.getUserId()); + } + return baseMapper.selectVoList(queryBo); + } + + /** + * 新增学生月度实习总结 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean insertByBo(ZsjyInternshipMonthlyAddBo addBo) { + Long studentId = LoginHelper.getUserId(); + + // 如果前端没有传递实习ID,则自动获取当前学生的实习ID + if (ObjectUtil.isNull(addBo.getInternshipId())) { + ZsjyInternshipInfo internshipInfo = internshipInfoMapper.selectOne( + new LambdaQueryWrapper() + .eq(ZsjyInternshipInfo::getStudentId, studentId) + .eq(ZsjyInternshipInfo::getStatus, "1") // 查询进行中状态的实习 + .eq(ZsjyInternshipInfo::getDeleted, 0) + .orderByDesc(ZsjyInternshipInfo::getCreateTime) + .last("LIMIT 1") + ); + + if (ObjectUtil.isNull(internshipInfo)) { + throw new ServiceException("您当前没有进行中的实习记录,无法创建月度总结"); + } + + addBo.setInternshipId(internshipInfo.getInternshipId()); + log.info("自动获取到学生实习ID:{}", internshipInfo.getInternshipId()); + } + + // 检查该学生该月份是否已有总结 + if (checkMonthExists(studentId, addBo.getSummaryMonth(), null)) { + throw new ServiceException("该月份已存在总结记录,请勿重复创建"); + } + + // 手动转换BO到实体,解决字段映射问题 + ZsjyInternshipMonthly add = new ZsjyInternshipMonthly(); + add.setId(addBo.getMonthlyId()); // monthlyId -> id + add.setInternshipId(addBo.getInternshipId()); + add.setSummaryMonth(addBo.getSummaryMonth()); + add.setWorkContent(addBo.getWorkContent()); + add.setAchievements(addBo.getAchievements()); + add.setLearningGains(addBo.getLearningGains()); + add.setProblemsEncountered(addBo.getProblemsEncountered()); + add.setSelfEvaluation(addBo.getSelfEvaluation()); + add.setNextMonthPlan(addBo.getNextMonthPlan()); + add.setProfessionalSkills(addBo.getProfessionalSkills()); + add.setCommunicationSkills(addBo.getCommunicationSkills()); + add.setTeamworkAbility(addBo.getTeamworkAbility()); + add.setLearningAbility(addBo.getLearningAbility()); + add.setWorkAttitude(addBo.getWorkAttitude()); + add.setAttachments(addBo.getAttachments()); + + add.setStudentId(studentId); + add.setTenantId("000000"); // 默认租户 + + // 处理审核状态和提交时间 + if (addBo.getReviewStatus() != null) { + add.setReviewStatus(addBo.getReviewStatus()); + // 如果状态为待审核(0),设置提交时间 + if (addBo.getReviewStatus() == 0) { + add.setSubmitTime(addBo.getSubmitTime() != null ? addBo.getSubmitTime() : new Date()); + } + } else { + add.setReviewStatus(3); // 默认为草稿状态 + } + + // 解析月份序号 + if (StringUtils.isNotBlank(addBo.getSummaryMonth())) { + String[] parts = addBo.getSummaryMonth().split("-"); + if (parts.length == 2) { + add.setMonthNumber(Integer.parseInt(parts[1])); + } + } + + // 计算字数 + add.setWordCount(calculateWordCount(addBo)); + + // 设置默认截止日期(下月5号) + try { + YearMonth ym = YearMonth.parse(addBo.getSummaryMonth()); + add.setSubmitDeadline(Date.from(ym.plusMonths(1).atDay(5).atStartOfDay(ZoneId.systemDefault()).toInstant())); + } catch (Exception e) { + log.warn("解析月份失败: {}", addBo.getSummaryMonth()); + } + + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + addBo.setMonthlyId(add.getId()); + } + return flag; + } + + /** + * 修改学生月度实习总结 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(ZsjyInternshipMonthlyAddBo addBo) { + Long studentId = LoginHelper.getUserId(); + + // 查询原记录,验证权限和状态 + ZsjyInternshipMonthly original = baseMapper.selectById(addBo.getMonthlyId()); + if (ObjectUtil.isNull(original)) { + throw new ServiceException("月度总结不存在"); + } + + // 权限检查:只能修改自己的总结 + if (!Objects.equals(original.getStudentId(), studentId)) { + throw new ServiceException("您只能修改自己的月度总结"); + } + + // 状态检查:只有草稿状态才能修改 + if (!Objects.equals(original.getReviewStatus(), 3)) { + throw new ServiceException("只有草稿状态的总结才能修改"); + } + + // 如果前端没有传递实习ID,则使用原记录的实习ID + if (ObjectUtil.isNull(addBo.getInternshipId())) { + addBo.setInternshipId(original.getInternshipId()); + log.info("使用原记录的实习ID:{}", original.getInternshipId()); + } + + // 检查月份是否被其他记录占用 + if (!Objects.equals(original.getSummaryMonth(), addBo.getSummaryMonth())) { + if (checkMonthExists(studentId, addBo.getSummaryMonth(), addBo.getMonthlyId())) { + throw new ServiceException("该月份已存在其他总结记录"); + } + } + + // 手动转换BO到实体,解决字段映射问题 + ZsjyInternshipMonthly update = new ZsjyInternshipMonthly(); + update.setId(addBo.getMonthlyId()); // monthlyId -> id + update.setInternshipId(addBo.getInternshipId()); + update.setSummaryMonth(addBo.getSummaryMonth()); + update.setWorkContent(addBo.getWorkContent()); + update.setAchievements(addBo.getAchievements()); + update.setLearningGains(addBo.getLearningGains()); + update.setProblemsEncountered(addBo.getProblemsEncountered()); + update.setSelfEvaluation(addBo.getSelfEvaluation()); + update.setNextMonthPlan(addBo.getNextMonthPlan()); + update.setProfessionalSkills(addBo.getProfessionalSkills()); + update.setCommunicationSkills(addBo.getCommunicationSkills()); + update.setTeamworkAbility(addBo.getTeamworkAbility()); + update.setLearningAbility(addBo.getLearningAbility()); + update.setWorkAttitude(addBo.getWorkAttitude()); + update.setAttachments(addBo.getAttachments()); + + // 处理审核状态和提交时间 + if (addBo.getReviewStatus() != null) { + update.setReviewStatus(addBo.getReviewStatus()); + // 如果状态从草稿(3)变为待审核(0),设置提交时间 + if (addBo.getReviewStatus() == 0 && original.getReviewStatus() == 3) { + update.setSubmitTime(addBo.getSubmitTime() != null ? addBo.getSubmitTime() : new Date()); + } + // 如果状态变回草稿(3),清空提交时间 + else if (addBo.getReviewStatus() == 3) { + update.setSubmitTime(null); + } + } + + // 解析月份序号 + if (StringUtils.isNotBlank(addBo.getSummaryMonth())) { + String[] parts = addBo.getSummaryMonth().split("-"); + if (parts.length == 2) { + update.setMonthNumber(Integer.parseInt(parts[1])); + } + } + + // 重新计算字数 + update.setWordCount(calculateWordCount(addBo)); + + return baseMapper.updateById(update) > 0; + } + + /** + * 批量删除学生月度实习总结 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteByIds(Collection ids) { + Long studentId = LoginHelper.getUserId(); + + // 验证权限:只能删除自己的草稿状态总结 + for (Long id : ids) { + ZsjyInternshipMonthly monthly = baseMapper.selectById(id); + if (ObjectUtil.isNull(monthly)) { + continue; + } + + if (!Objects.equals(monthly.getStudentId(), studentId)) { + throw new ServiceException("您只能删除自己的月度总结"); + } + + if (!Objects.equals(monthly.getReviewStatus(), 3)) { + throw new ServiceException("只有草稿状态的总结才能删除"); + } + } + + return baseMapper.deleteByIds(ids.toArray(new Long[0])) > 0; + } + + /** + * 提交月度总结审批 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean submitForApproval(Long monthlyId) { + Long studentId = LoginHelper.getUserId(); + + ZsjyInternshipMonthly monthly = baseMapper.selectById(monthlyId); + if (ObjectUtil.isNull(monthly)) { + throw new ServiceException("月度总结不存在"); + } + + // 权限检查 + if (!Objects.equals(monthly.getStudentId(), studentId)) { + throw new ServiceException("您只能提交自己的月度总结"); + } + + // 状态检查 + if (!Objects.equals(monthly.getReviewStatus(), 3)) { + throw new ServiceException("只有草稿状态的总结才能提交"); + } + + // 必填字段检查 + if (StringUtils.isBlank(monthly.getWorkContent())) { + throw new ServiceException("工作内容不能为空"); + } + + // 更新状态为待审核 + ZsjyInternshipMonthly update = new ZsjyInternshipMonthly(); + update.setId(monthlyId); + update.setReviewStatus(0); // 待审核 + update.setSubmitTime(new Date()); + + return baseMapper.updateById(update) > 0; + } + + /** + * 自动保存草稿 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean autoSaveDraft(ZsjyInternshipMonthlyAddBo addBo) { + try { + if (ObjectUtil.isNull(addBo.getMonthlyId())) { + // 新增草稿 + return insertByBo(addBo); + } else { + // 更新草稿 + return updateByBo(addBo); + } + } catch (Exception e) { + log.warn("自动保存草稿失败: {}", e.getMessage()); + return false; + } + } + + /** + * 检查学生某月是否已有总结 + */ + @Override + public Boolean checkMonthExists(Long studentId, String summaryMonth, Long monthlyId) { + return baseMapper.existsByStudentAndMonth(studentId, summaryMonth, monthlyId); + } + + /** + * 获取月度总结模板信息 + */ + @Override + public ZsjyInternshipMonthlyVo getMonthlyTemplate(String summaryMonth) { + ZsjyInternshipMonthlyVo template = new ZsjyInternshipMonthlyVo(); + template.setSummaryMonth(summaryMonth); + + // 解析月份序号 + if (StringUtils.isNotBlank(summaryMonth)) { + String[] parts = summaryMonth.split("-"); + if (parts.length == 2) { + template.setMonthNumber(Integer.parseInt(parts[1])); + } + } + + // 设置默认截止日期(下月5号) + try { + YearMonth ym = YearMonth.parse(summaryMonth); + template.setSubmitDeadline(Date.from(ym.plusMonths(1).atDay(5).atStartOfDay(ZoneId.systemDefault()).toInstant())); + } catch (Exception e) { + log.warn("解析月份失败: {}", summaryMonth); + } + + template.setReviewStatus(3); // 草稿 + template.setStatusText("草稿"); + template.setEditable(true); + template.setDeletable(true); + template.setSubmittable(true); + + return template; + } + + /** + * 查询学生指定实习的月度总结列表 + */ + @Override + public List queryByStudentAndInternship(Long studentId, Long internshipId) { + if (ObjectUtil.isNull(studentId)) { + studentId = LoginHelper.getUserId(); + } + return baseMapper.selectByStudentAndInternship(studentId, internshipId); + } + + /** + * 统计学生各状态的总结数量 + */ + @Override + public List countByStatus(Long studentId) { + if (ObjectUtil.isNull(studentId)) { + studentId = LoginHelper.getUserId(); + } + return baseMapper.countByStatus(studentId); + } + + /** + * 计算总结内容字数 + */ + @Override + public Integer calculateWordCount(ZsjyInternshipMonthlyAddBo addBo) { + int wordCount = 0; + + if (StringUtils.isNotBlank(addBo.getWorkContent())) { + wordCount += addBo.getWorkContent().length(); + } + if (StringUtils.isNotBlank(addBo.getAchievements())) { + wordCount += addBo.getAchievements().length(); + } + if (StringUtils.isNotBlank(addBo.getLearningGains())) { + wordCount += addBo.getLearningGains().length(); + } + if (StringUtils.isNotBlank(addBo.getProblemsEncountered())) { + wordCount += addBo.getProblemsEncountered().length(); + } + if (StringUtils.isNotBlank(addBo.getSelfEvaluation())) { + wordCount += addBo.getSelfEvaluation().length(); + } + if (StringUtils.isNotBlank(addBo.getNextMonthPlan())) { + wordCount += addBo.getNextMonthPlan().length(); + } + + return wordCount; + } + + /** + * 获取学生当前实习信息 + */ + @Override + public Map getCurrentInternshipInfo() { + Long currentUserId = LoginHelper.getUserId(); + log.info("获取学生当前实习信息, 用户ID: {}", currentUserId); + + // 查询学生当前进行中的实习记录(与insertByBo方法保持一致) + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(ZsjyInternshipInfo::getStudentId, currentUserId) + .eq(ZsjyInternshipInfo::getStatus, "1") // 查询进行中状态的实习 + .eq(ZsjyInternshipInfo::getDeleted, 0) + .orderByDesc(ZsjyInternshipInfo::getCreateTime) + .last("LIMIT 1"); + + ZsjyInternshipInfo internshipInfo = internshipInfoMapper.selectOne(wrapper); + + if (internshipInfo == null) { + log.warn("学生{}没有找到进行中的实习记录", currentUserId); + return new HashMap<>(); + } + + Map result = new HashMap<>(); + result.put("internshipId", internshipInfo.getInternshipId()); + result.put("companyName", internshipInfo.getCompanyName()); + result.put("position", internshipInfo.getPositionName()); + result.put("startDate", internshipInfo.getStartDate()); + result.put("endDate", internshipInfo.getEndDate()); + + log.info("找到学生实习信息: {}", result); + return result; + } + +} diff --git a/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipMonthlyMapper.xml b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipMonthlyMapper.xml new file mode 100644 index 00000000..be52f67b --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-zsjy/src/main/resources/mapper/ZsjyInternshipMonthlyMapper.xml @@ -0,0 +1,240 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT + m.monthly_id, + m.internship_id, + m.student_id, + u.nick_name as student_name, + ui.student_no as student_number, + m.summary_month, + m.month_number, + m.work_content, + m.achievements, + m.learning_gains, + m.problems_encountered, + m.self_evaluation, + m.next_month_plan, + m.professional_skills, + m.communication_skills, + m.teamwork_ability, + m.learning_ability, + m.work_attitude, + m.review_status, + CASE m.review_status + WHEN 0 THEN '待审核' + WHEN 1 THEN '已审核' + WHEN 2 THEN '审核拒绝' + WHEN 3 THEN '草稿' + ELSE '未知状态' + END as status_text, + m.review_by, + r.nick_name as reviewer_name, + m.review_time, + m.review_score, + m.review_comment as review_comments, + m.submit_time, + m.word_count, + m.is_overdue, + m.submit_deadline, + m.attachments, + CASE WHEN m.review_status = 3 THEN 1 ELSE 0 END as editable, + CASE WHEN m.review_status = 3 THEN 1 ELSE 0 END as deletable, + CASE WHEN m.review_status = 3 THEN 1 ELSE 0 END as submittable, + m.create_by, + m.create_time, + m.update_by, + m.update_time + FROM zsjy_internship_monthly m + LEFT JOIN sys_user u ON u.user_id = m.student_id + LEFT JOIN zsjy_internship_info ui ON ui.internship_id = m.internship_id AND ui.student_id = m.student_id + LEFT JOIN sys_user r ON r.user_id = m.review_by + WHERE m.deleted = 0 + + + + + + + + + + + + UPDATE zsjy_internship_monthly SET deleted = 1 + WHERE monthly_id IN + + #{monthlyId} + + AND review_status = 3 + + + + + + + diff --git a/mobile/src/api/internship.js b/mobile/src/api/internship.js index ad4b77dc..304cecb7 100644 --- a/mobile/src/api/internship.js +++ b/mobile/src/api/internship.js @@ -28,6 +28,17 @@ export const getStudentInternshipInfo = () => { }); }; +/** + * 获取当前学生实习信息(新版API) + * @returns Promise + */ +export const getCurrentStudentInternship = () => { + return request({ + url: '/zsjy/internship/current', + method: 'GET' + }); +}; + /** * 学生签到打卡 * @param {Object} data 签到数据 diff --git a/mobile/src/pages/index/index.vue b/mobile/src/pages/index/index.vue index f92596ef..c1f34640 100644 --- a/mobile/src/pages/index/index.vue +++ b/mobile/src/pages/index/index.vue @@ -170,7 +170,7 @@