提交代码

This commit is contained in:
zlwu committed 2025-09-06 20:42:14 +08:00
1 parent 25892480cc
commit 718ab4cb38
12 files changed
+1804 -309

No files matched your search

@@ -23,6 +23,7 @@ import org.dromara.xg.domain.bo.XgDormHygieneCheckBo;
import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo;
import org.dromara.xg.service.IXgDormHygieneCheckService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
/**
* 宿舍卫生检查
@@ -175,4 +176,30 @@ public class XgDormHygieneCheckController extends BaseController {
return toAjax(xgDormHygieneCheckService.saveCompleteData(bo));
}
/**
* 更新完整的宿舍卫生检查数据(包含学生评分和违纪记录)
*/
@SaCheckPermission("dormHygieneCheck:dormHygieneCheck:edit")
@Log(title = "宿舍卫生检查", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/updateCompleteData")
public R<Void> updateCompleteData(@Validated(EditGroup.class) @RequestBody XgDormHygieneCheckCompleteBo bo) {
return toAjax(xgDormHygieneCheckService.updateCompleteData(bo));
}
/**
* 根据卫生检查ID查询相关的违纪记录
*
* @param hygieneCheckId 卫生检查ID
* @return 违纪记录列表
*/
@SaCheckPermission("dormHygieneCheck:dormHygieneCheck:query")
@GetMapping("/getViolations/{hygieneCheckId}")
public R<List<XgHygieneCheckViolationVo>> getViolations(
@NotNull(message = "卫生检查ID不能为空") @PathVariable Long hygieneCheckId) {
List<XgHygieneCheckViolationVo> violations = xgDormHygieneCheckService
.getViolationsByHygieneCheckId(hygieneCheckId);
return R.ok(violations);
}
}
@@ -74,4 +74,10 @@ public class XgHygieneCheckViolation extends TenantEntity {
*/
private String status;
/**
* 删除标志(0代表存在 1代表删除)
*/
@TableLogic(value = "0", delval = "1")
private Long deleted;
}
@@ -17,6 +17,16 @@ import java.math.BigDecimal;
@Data
public class XgViolationDataBo {
/**
* 违纪记录ID(用于更新操作,新增时为null)
*/
private Long id;
/**
* 违纪记录ID,关联xskh_discipline_record.id(用于更新违纪记录)
*/
private Long disciplineRecordId;
/**
* 学生ID,关联xg_student_msg.id
*/
@@ -5,6 +5,7 @@ import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.time.LocalDate;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
@@ -72,6 +73,34 @@ public class XgHygieneCheckViolationVo {
*/
private String status;
// ============ 新增违纪详情字段 ============
/**
* 违纪处分日期
*/
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate disciplineDate;
/**
* 违纪处分原因
*/
private String disciplineReason;
/**
* 违纪处分内容
*/
private String disciplineContent;
/**
* 扣分分值
*/
private BigDecimal scoreDeduction;
/**
* 附件OSS ID列表,逗号分隔
*/
private String attachmentOssIds;
/**
* 创建者
*/
@@ -3,6 +3,8 @@ package org.dromara.xg.mapper;
import org.dromara.xg.domain.XgHygieneCheckViolation;
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 宿舍卫生检查违纪记录Mapper接口
@@ -11,6 +13,14 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
* @date 2025-01-29
*/
public interface XgHygieneCheckViolationMapper
extends BaseMapperPlus<XgHygieneCheckViolation, XgHygieneCheckViolationVo> {
extends BaseMapperPlus<XgHygieneCheckViolation, XgHygieneCheckViolationVo> {
/**
* 根据卫生检查ID查询违纪记录(包含详情)
*
* @param hygieneCheckId 卫生检查ID
* @return 违纪记录列表(包含详情)
*/
List<XgHygieneCheckViolationVo> selectViolationsByHygieneCheckId(@Param("hygieneCheckId") Long hygieneCheckId);
}
@@ -2,6 +2,7 @@ package org.dromara.xg.service;
import org.dromara.xg.domain.vo.DormHygieneCheckExportVo;
import org.dromara.xg.domain.vo.XgDormHygieneCheckVo;
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
import org.dromara.xg.domain.bo.XgDormHygieneCheckBo;
import org.dromara.xg.domain.bo.XgDormHygieneCheckCompleteBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -119,4 +120,20 @@ public interface IXgDormHygieneCheckService {
*/
Boolean saveCompleteData(XgDormHygieneCheckCompleteBo bo);
/**
* 更新完整的宿舍卫生检查数据(包含学生评分和违纪记录)
*
* @param bo 完整的宿舍卫生检查数据
* @return 是否更新成功
*/
Boolean updateCompleteData(XgDormHygieneCheckCompleteBo bo);
/**
* 根据卫生检查ID查询相关的违纪记录
*
* @param hygieneCheckId 卫生检查ID
* @return 违纪记录列表
*/
List<XgHygieneCheckViolationVo> getViolationsByHygieneCheckId(Long hygieneCheckId);
}
@@ -25,6 +25,7 @@ import org.dromara.xg.mapper.XgDormHygieneStudentScoreMapper;
import org.dromara.xg.service.IXgDormHygieneCheckService;
import org.dromara.xg.service.IXgStudentMsgService;
import org.dromara.xg.service.IXgHygieneCheckViolationService;
import org.dromara.xg.mapper.XgHygieneCheckViolationMapper;
import org.dromara.system.service.ISysDictDataService;
import org.springframework.context.ApplicationContext;
@@ -32,6 +33,13 @@ import java.util.List;
import java.util.Map;
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Collectors;
import org.dromara.xg.domain.vo.XgHygieneCheckViolationVo;
import org.dromara.xg.domain.XgHygieneCheckViolation;
import java.util.Collections;
/**
* 宿舍卫生检查Service业务层处理
@@ -49,6 +57,7 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
private final IXgStudentMsgService xgStudentMsgService;
private final ISysDictDataService sysDictDataService;
private final IXgHygieneCheckViolationService violationService;
private final XgHygieneCheckViolationMapper violationMapper;
private final ApplicationContext applicationContext;
/**
@@ -708,7 +717,8 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
}
} catch (Exception e) {
log.error("处理违纪数据失败,学生ID: {}, 错误: {}", violationBo.getStudentId(), e.getMessage(), e);
// 不影响主流程,继续处理下一个
throw new RuntimeException(
"处理违纪数据失败,学生ID: " + violationBo.getStudentId() + ", 错误: " + e.getMessage(), e);
}
}
}
@@ -947,4 +957,344 @@ public class XgDormHygieneCheckServiceImpl implements IXgDormHygieneCheckService
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateCompleteData(XgDormHygieneCheckCompleteBo bo) {
try {
// 1. 更新主表数据
XgDormHygieneCheck update = MapstructUtils.convert(bo, XgDormHygieneCheck.class);
validEntityBeforeSave(update);
boolean flag = baseMapper.updateById(update) > 0;
log.info("更新宿舍卫生检查主表数据: {}", update);
if (!flag) {
log.error("更新宿舍卫生检查主表数据失败");
return false;
}
Long hygieneCheckId = update.getId();
// 2. 更新学生评分数据 - 先删除旧数据,再插入新数据
LambdaQueryWrapper<XgDormHygieneStudentScore> scoreWrapper = Wrappers.lambdaQuery();
scoreWrapper.eq(XgDormHygieneStudentScore::getHygieneCheckId, hygieneCheckId);
studentScoreMapper.delete(scoreWrapper);
log.info("删除旧的学生评分数据,检查ID: {}", hygieneCheckId);
if (bo.getStudentScores() != null && !bo.getStudentScores().isEmpty()) {
for (XgDormHygieneStudentScoreBo studentScoreBo : bo.getStudentScores()) {
XgDormHygieneStudentScore studentScore = MapstructUtils.convert(studentScoreBo,
XgDormHygieneStudentScore.class);
studentScore.setHygieneCheckId(hygieneCheckId);
studentScore.setId(null); // 确保是新插入
// 根据学生ID查询学生完整信息,补充学院、专业、班级信息
if (studentScoreBo.getStudentId() != null) {
try {
var studentInfo = xgStudentMsgService.queryById(studentScoreBo.getStudentId());
if (studentInfo != null) {
// 补充学院信息
if (studentScore.getCollegeId() == null
&& StringUtils.isNotBlank(studentInfo.getCollegeId())) {
try {
studentScore.setCollegeId(Long.valueOf(studentInfo.getCollegeId()));
} catch (NumberFormatException e) {
log.warn("学院ID转换失败,学生ID: {}, 学院ID: {}", studentScoreBo.getStudentId(),
studentInfo.getCollegeId());
}
}
if (StringUtils.isBlank(studentScore.getCollegeName())) {
studentScore.setCollegeName(studentInfo.getCollegeName());
}
// 补充专业信息
if (studentScore.getMajorId() == null
&& StringUtils.isNotBlank(studentInfo.getMajorId())) {
try {
studentScore.setMajorId(Long.valueOf(studentInfo.getMajorId()));
} catch (NumberFormatException e) {
log.warn("专业ID转换失败,学生ID: {}, 专业ID: {}", studentScoreBo.getStudentId(),
studentInfo.getMajorId());
}
}
if (StringUtils.isBlank(studentScore.getMajorName())) {
studentScore.setMajorName(studentInfo.getMajorName());
}
// 补充班级信息
if (studentScore.getClassId() == null
&& StringUtils.isNotBlank(studentInfo.getClassId())) {
try {
studentScore.setClassId(Long.valueOf(studentInfo.getClassId()));
} catch (NumberFormatException e) {
log.warn("班级ID转换失败,学生ID: {}, 班级ID: {}", studentScoreBo.getStudentId(),
studentInfo.getClassId());
}
}
if (StringUtils.isBlank(studentScore.getClassName())) {
studentScore.setClassName(studentInfo.getClassName());
}
log.info("补充学生信息 - 学生ID: {}, 学院: {}, 专业: {}, 班级: {}",
studentScoreBo.getStudentId(),
studentInfo.getCollegeName(),
studentInfo.getMajorName(),
studentInfo.getClassName());
}
} catch (Exception e) {
log.warn("查询学生信息失败,学生ID: {}, 错误: {}", studentScoreBo.getStudentId(), e.getMessage());
}
}
log.info("保存学生评分数据: {}", studentScore);
studentScoreMapper.insert(studentScore);
}
}
// 3. 处理违纪记录数据 - 完整的增删改逻辑
handleViolationRecords(hygieneCheckId, bo.getViolationData());
log.info("完整更新宿舍卫生检查数据成功,检查ID: {}", hygieneCheckId);
return true;
} catch (Exception e) {
log.error("更新宿舍卫生检查数据失败", e);
throw new RuntimeException("更新宿舍卫生检查数据失败: " + e.getMessage(), e);
}
}
/**
* 处理违纪记录数据 - 完整的增删改逻辑
*
* @param hygieneCheckId 卫生检查ID
* @param newViolationData 新的违纪记录数据列表
*/
private void handleViolationRecords(Long hygieneCheckId, List<XgViolationDataBo> newViolationData) {
try {
// 1. 查询数据库中现有的违纪记录
List<XgHygieneCheckViolationVo> existingViolations = getViolationsByHygieneCheckId(hygieneCheckId);
log.info("数据库中现有违纪记录数量: {}", existingViolations.size());
// 2. 收集现有记录的违纪记录ID(disciplineRecordId)
Set<Long> existingDisciplineIds = existingViolations.stream()
.map(XgHygieneCheckViolationVo::getDisciplineRecordId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
log.info("现有违纪记录ID集合: {}", existingDisciplineIds);
// 3. 收集新数据中的违纪记录ID(优先使用disciplineRecordId字段,兼容旧版本的id字段)
Set<Long> newDisciplineIds = new HashSet<>();
if (newViolationData != null) {
newDisciplineIds = newViolationData.stream()
.map(violationBo -> {
// 优先使用disciplineRecordId字段,如果为空则使用id字段(向后兼容)
return violationBo.getDisciplineRecordId() != null
? violationBo.getDisciplineRecordId()
: violationBo.getId();
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
log.info("新数据中的违纪记录ID集合: {}", newDisciplineIds);
// 4. 找出需要删除的记录(在数据库中存在,但不在新数据中)
Set<Long> toDeleteIds = new HashSet<>(existingDisciplineIds);
toDeleteIds.removeAll(newDisciplineIds);
log.info("需要删除的违纪记录ID集合: {}", toDeleteIds);
// 5. 删除不再存在的记录
for (Long deleteId : toDeleteIds) {
deleteViolationRecord(hygieneCheckId, deleteId);
}
// 6. 处理新数据中的记录(新增或更新)
if (newViolationData != null && !newViolationData.isEmpty()) {
for (XgViolationDataBo violationBo : newViolationData) {
try {
log.info("处理违纪记录数据: {}", violationBo);
// 如果是已存在的违纪记录(有违纪记录ID且在现有记录中),更新统一表中的记录
Long currentDisciplineRecordId = violationBo.getDisciplineRecordId() != null
? violationBo.getDisciplineRecordId()
: violationBo.getId();
if (currentDisciplineRecordId != null
&& existingDisciplineIds.contains(currentDisciplineRecordId)) {
log.info("更新现有违纪记录,违纪记录ID: {}", currentDisciplineRecordId);
updateDisciplineRecord(violationBo);
} else {
// 新增违纪记录
log.info("新增违纪记录,学生ID: {}", violationBo.getStudentId());
Long disciplineRecordId = createDisciplineRecord(violationBo);
if (disciplineRecordId != null && violationService != null) {
XgHygieneCheckViolationBo relationBo = new XgHygieneCheckViolationBo();
relationBo.setHygieneCheckId(hygieneCheckId);
relationBo.setDisciplineRecordId(disciplineRecordId);
relationBo.setStudentId(violationBo.getStudentId());
relationBo.setStudentNo(violationBo.getStudentNo());
relationBo.setStudentName(violationBo.getStudentName());
relationBo.setDisciplineTypeId(violationBo.getDisciplineTypeId());
relationBo.setDisciplineTypeName(violationBo.getDisciplineTypeName());
relationBo.setViolationSource("1"); // 1-宿舍卫生检查
relationBo.setStatus("1"); // 1-生效
boolean relationResult = violationService.insertByBo(relationBo);
if (!relationResult) {
log.warn("创建卫生检查违纪关联记录失败,学生ID: {}", violationBo.getStudentId());
} else {
log.info("创建卫生检查违纪关联记录成功,学生ID: {},违纪记录ID: {}",
violationBo.getStudentId(), disciplineRecordId);
}
}
}
} catch (Exception e) {
log.error("处理违纪数据失败,学生ID: {}, 错误: {}", violationBo.getStudentId(), e.getMessage(), e);
throw new RuntimeException(
"处理违纪数据失败,学生ID: " + violationBo.getStudentId() + ", 错误: " + e.getMessage(), e);
}
}
}
log.info("违纪记录处理完成,卫生检查ID: {}", hygieneCheckId);
} catch (Exception e) {
log.error("处理违纪记录数据失败,卫生检查ID: {}, 错误: {}", hygieneCheckId, e.getMessage(), e);
throw new RuntimeException("处理违纪记录数据失败: " + e.getMessage(), e);
}
}
/**
* 删除违纪记录(同时删除关联表和统一表中的记录)
*
* @param hygieneCheckId 卫生检查ID
* @param disciplineRecordId 违纪记录ID
*/
private void deleteViolationRecord(Long hygieneCheckId, Long disciplineRecordId) {
try {
log.info("开始逻辑删除违纪记录,卫生检查ID: {}, 违纪记录ID: {}", hygieneCheckId, disciplineRecordId);
// 1. 逻辑删除关联表中的记录 (xg_hygiene_check_violation)
if (violationMapper != null) {
LambdaQueryWrapper<XgHygieneCheckViolation> wrapper = Wrappers.lambdaQuery();
wrapper.eq(XgHygieneCheckViolation::getHygieneCheckId, hygieneCheckId)
.eq(XgHygieneCheckViolation::getDisciplineRecordId, disciplineRecordId);
// 使用逻辑删除
int deleteCount = violationMapper.delete(wrapper);
if (deleteCount > 0) {
log.info("逻辑删除卫生检查违纪关联记录成功,违纪记录ID: {}, 删除数量: {}", disciplineRecordId, deleteCount);
} else {
log.warn("逻辑删除卫生检查违纪关联记录失败,违纪记录ID: {}", disciplineRecordId);
}
}
// 2. 逻辑删除统一表中的记录 (xskh_discipline_record)
deleteDisciplineRecord(disciplineRecordId);
} catch (Exception e) {
log.error("逻辑删除违纪记录失败,违纪记录ID: {}, 错误: {}", disciplineRecordId, e.getMessage(), e);
throw new RuntimeException("逻辑删除违纪记录失败: " + e.getMessage(), e);
}
}
/**
* 逻辑删除统一表中的违纪记录
*
* @param disciplineRecordId 违纪记录ID
*/
private void deleteDisciplineRecord(Long disciplineRecordId) {
try {
// 通过接口类型动态获取IXskhDisciplineRecordService,避免硬依赖
Class<?> serviceInterface = Class.forName("org.dromara.xskh.service.IXskhDisciplineRecordService");
Object disciplineRecordService = applicationContext.getBean(serviceInterface);
if (disciplineRecordService != null) {
// 使用deleteWithValidByIds方法进行逻辑删除
var deleteMethod = disciplineRecordService.getClass().getMethod("deleteWithValidByIds",
Collection.class, Boolean.class);
Boolean result = (Boolean) deleteMethod.invoke(disciplineRecordService,
Collections.singletonList(disciplineRecordId), false);
if (result) {
log.info("逻辑删除统一违纪记录成功,违纪记录ID: {}", disciplineRecordId);
} else {
log.error("逻辑删除统一违纪记录失败,违纪记录ID: {}", disciplineRecordId);
}
} else {
log.error("未找到违纪记录服务,无法删除统一违纪记录,违纪记录ID: {}", disciplineRecordId);
}
} catch (ClassNotFoundException e) {
log.warn("学生考核管理模块(xskh)未启用,跳过统一违纪记录删除");
} catch (Exception e) {
log.error("逻辑删除统一违纪记录失败,违纪记录ID: {}, 错误: {}", disciplineRecordId, e.getMessage(), e);
throw new RuntimeException("逻辑删除统一违纪记录失败: " + e.getMessage(), e);
}
}
/**
* 更新学生违纪记录
*
* @param violationBo 违纪数据
*/
private void updateDisciplineRecord(XgViolationDataBo violationBo) {
try {
// 通过接口类型动态获取IXskhDisciplineRecordService,避免硬依赖
Class<?> serviceInterface = Class.forName("org.dromara.xskh.service.IXskhDisciplineRecordService");
Object disciplineRecordService = applicationContext.getBean(serviceInterface);
if (disciplineRecordService != null) {
// 创建XskhDisciplineRecordBo对象
Object disciplineRecordBo = createDisciplineRecordBo(violationBo);
if (disciplineRecordBo != null) {
// 设置ID用于更新
setFieldValue(disciplineRecordBo, "id", violationBo.getId());
// 反射调用updateByBo方法
Class<?> disciplineRecordBoClass = Class
.forName("org.dromara.xskh.domain.bo.XskhDisciplineRecordBo");
var updateMethod = disciplineRecordService.getClass().getMethod("updateByBo",
disciplineRecordBoClass);
Boolean result = (Boolean) updateMethod.invoke(disciplineRecordService, disciplineRecordBo);
if (result) {
log.info("更新学生违纪记录成功,学生ID: {}, 违纪记录ID: {}",
violationBo.getStudentId(), violationBo.getId());
} else {
log.warn("更新学生违纪记录失败,学生ID: {}", violationBo.getStudentId());
}
}
}
} catch (ClassNotFoundException e) {
log.warn("学生考核管理模块(xskh)未启用,跳过违纪记录更新");
} catch (Exception e) {
log.error("更新学生违纪记录失败: {}", e.getMessage(), e);
}
}
/**
* 根据卫生检查ID查询相关的违纪记录(包含详情)
*
* @param hygieneCheckId 卫生检查ID
* @return 违纪记录列表(包含详情)
*/
@Override
public List<XgHygieneCheckViolationVo> getViolationsByHygieneCheckId(Long hygieneCheckId) {
if (hygieneCheckId == null) {
log.warn("卫生检查ID为空,无法查询违纪记录");
return List.of();
}
try {
// 使用自定义查询方法,关联查询违纪详情
List<XgHygieneCheckViolationVo> violations = violationMapper
.selectViolationsByHygieneCheckId(hygieneCheckId);
log.info("卫生检查ID: {} 查询到 {} 条违纪记录(包含详情)", hygieneCheckId, violations.size());
return violations;
} catch (Exception e) {
log.error("查询卫生检查违纪记录失败,检查ID: {}, 错误: {}", hygieneCheckId, e.getMessage(), e);
return List.of();
}
}
}
@@ -0,0 +1,78 @@
<?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.XgHygieneCheckViolationMapper">
<resultMap type="org.dromara.xg.domain.vo.XgHygieneCheckViolationVo" id="XgHygieneCheckViolationResult">
<result property="id" column="id" />
<result property="hygieneCheckId" column="hygiene_check_id" />
<result property="disciplineRecordId" column="discipline_record_id" />
<result property="studentId" column="student_id" />
<result property="studentNo" column="student_no" />
<result property="studentName" column="student_name" />
<result property="disciplineTypeId" column="discipline_type_id" />
<result property="disciplineTypeName" column="discipline_type_name" />
<result property="violationSource" column="violation_source" />
<result property="status" column="status" />
<!-- 违纪详情字段 -->
<result property="disciplineDate" column="discipline_date" />
<result property="disciplineReason" column="discipline_reason" />
<result property="disciplineContent" column="discipline_content" />
<result property="scoreDeduction" column="score_deduction" />
<result property="attachmentOssIds" column="attachment_oss_ids" />
<result property="createBy" column="create_by" />
<result property="createTime" column="create_time" />
<result property="updateBy" column="update_by" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectXgHygieneCheckViolationVo">
SELECT
hcv.id,
hcv.hygiene_check_id,
hcv.discipline_record_id,
hcv.student_id,
hcv.student_no,
hcv.student_name,
hcv.discipline_type_id,
hcv.discipline_type_name,
hcv.violation_source,
hcv.status,
hcv.create_by,
hcv.create_time,
hcv.update_by,
hcv.update_time,
-- 关联违纪详情
dr.discipline_date,
dr.discipline_reason,
dr.discipline_content,
dr.score_deduction,
dr.attachment_oss_ids
FROM xg_hygiene_check_violation hcv
LEFT JOIN xskh_discipline_record dr ON hcv.discipline_record_id = dr.id AND dr.deleted = 0
</sql>
<select id="selectVoList" parameterType="com.baomidou.mybatisplus.core.conditions.Wrapper" resultMap="XgHygieneCheckViolationResult">
<include refid="selectXgHygieneCheckViolationVo"/>
<where>
hcv.deleted = 0
${ew.customSqlSegment}
</where>
ORDER BY hcv.create_time DESC
</select>
<select id="selectVoById" parameterType="Long" resultMap="XgHygieneCheckViolationResult">
<include refid="selectXgHygieneCheckViolationVo"/>
WHERE hcv.id = #{id} AND hcv.deleted = 0
</select>
<!-- 根据卫生检查ID查询违纪记录(包含详情) -->
<select id="selectViolationsByHygieneCheckId" parameterType="Long" resultMap="XgHygieneCheckViolationResult">
<include refid="selectXgHygieneCheckViolationVo"/>
WHERE hcv.hygiene_check_id = #{hygieneCheckId}
AND hcv.deleted = 0
ORDER BY hcv.create_time DESC
</select>
</mapper>
@@ -412,4 +412,117 @@ lqw.orderByAsc(XgDormRoom::getSort); // 第二优先级:按排序号升序
#### 涉及文件
- `web/src/views/xg/xgss/dormHygieneCheck/dormHygieneCheck/DormHygieneCheckDialog.vue`
- `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java`
- `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormRoomServiceImpl.java`
---
## 违纪记录更新修复 (2025-01-03)
### 问题描述
在调用 `updateCompleteData` 接口更新宿舍卫生检查数据时,即使违纪记录入参中包含有效的 `disciplineRecordId`,系统仍然执行了"逻辑删除旧记录 + 新增新记录"的操作,而不是直接更新现有记录。
### 根本原因
**数据字段映射错误**:
- `XgViolationDataBo` 中的 `id` 字段被误认为是违纪记录ID (`disciplineRecordId`)
- 实际上 `id` 字段是关联表 `xg_hygiene_check_violation` 的主键ID
- 真正的违纪记录ID应该通过 `disciplineRecordId` 字段传递
### 修复方案
#### 1. 扩展 XgViolationDataBo 字段
**文件**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgViolationDataBo.java`
```java
/**
* 违纪记录ID(用于更新操作,新增时为null)
*/
private Long id;
/**
* 违纪记录ID,关联xskh_discipline_record.id(用于更新违纪记录)
*/
private Long disciplineRecordId;
```
#### 2. 修复 handleViolationRecords 方法逻辑
**文件**: `backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgDormHygieneCheckServiceImpl.java`
**修复前**:
```java
newDisciplineIds = newViolationData.stream()
.map(XgViolationDataBo::getId) // 错误:使用关联表ID
.filter(Objects::nonNull)
.collect(Collectors.toSet());
```
**修复后**:
```java
newDisciplineIds = newViolationData.stream()
.map(violationBo -> {
// 优先使用disciplineRecordId字段,如果为空则使用id字段(向后兼容)
return violationBo.getDisciplineRecordId() != null
? violationBo.getDisciplineRecordId()
: violationBo.getId();
})
.filter(Objects::nonNull)
.collect(Collectors.toSet());
```
#### 3. 修复判断逻辑
**修复前**:
```java
if (violationBo.getId() != null && existingDisciplineIds.contains(violationBo.getId())) {
// 更新逻辑
}
```
**修复后**:
```java
Long currentDisciplineRecordId = violationBo.getDisciplineRecordId() != null
? violationBo.getDisciplineRecordId()
: violationBo.getId();
if (currentDisciplineRecordId != null && existingDisciplineIds.contains(currentDisciplineRecordId)) {
// 更新逻辑
}
```
### 正确的入参格式
```json
{
"violationData": [
{
"id": "1964301377652428802", // 关联表ID (可选)
"disciplineRecordId": "1964301377522405377", // 违纪记录ID (用于更新判断)
"studentId": "1949114038914949121",
"studentNo": "2025DQ21121",
"studentName": "梁文清",
"disciplineTypeId": "1963793291531026434",
"disciplineTypeName": "地面未打扫",
"disciplineReason": "未按要求打扫地面",
"disciplineContent": "地面有明显垃圾",
"scoreDeduction": "2.00",
// ... 其他字段
}
]
}
```
### 修复效果
1. **准确识别更新操作**:系统能正确识别哪些违纪记录需要更新,哪些需要新增
2. **避免重复数据**:不再错误地删除现有记录并新增重复记录
3. **向后兼容**:保持对旧版本数据格式的兼容性
4. **数据完整性**:确保违纪记录的历史数据完整性
### 测试验证
使用修复后的接口,当入参包含 `disciplineRecordId` 时:
- ✅ 系统识别为更新操作,直接更新现有违纪记录
- ✅ 不再执行逻辑删除 + 新增的错误流程
- ✅ 数据库中不会产生重复的违纪记录
File diff suppressed because it is too large. Load diff
@@ -7,7 +7,7 @@
<span class="student-name">{{ studentInfo?.studentName }}</span>
<span class="student-detail">({{ studentInfo?.studentNo }})</span>
<span class="student-detail">{{ studentInfo?.className }}</span>
<span class="student-detail">{{ studentInfo?.bedName }}</span>
<span class="student-detail">{{ studentInfo?.bedCode }}</span>
</div>
</el-form-item>
@@ -167,7 +167,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch, onMounted } from 'vue';
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import { Plus, InfoFilled, UploadFilled, Document, Loading, Download, Delete } from '@element-plus/icons-vue';
// import ImagePreview from '@/components/ImagePreview/index.vue';
@@ -367,7 +367,42 @@ const initForm = () => {
if (props.violationData) {
console.log('违纪记录:编辑模式,violationData:', props.violationData);
Object.assign(form, props.violationData);
// 处理违纪数据回显
const violationDataCopy = { ...props.violationData };
// 确保disciplineTypeId是字符串类型(Element Plus select组件需要)
if (violationDataCopy.disciplineTypeId !== null && violationDataCopy.disciplineTypeId !== undefined) {
violationDataCopy.disciplineTypeId = violationDataCopy.disciplineTypeId.toString();
}
// 确保scoreDeduction是数值类型
if (violationDataCopy.scoreDeduction !== null && violationDataCopy.scoreDeduction !== undefined) {
violationDataCopy.scoreDeduction = parseFloat(violationDataCopy.scoreDeduction) || 0;
}
// 格式化日期(确保格式正确)
if (violationDataCopy.disciplineDate) {
// 如果是完整的日期时间,只取日期部分
const dateStr = violationDataCopy.disciplineDate.toString();
if (dateStr.includes(' ')) {
violationDataCopy.disciplineDate = dateStr.split(' ')[0];
} else if (dateStr.includes('T')) {
violationDataCopy.disciplineDate = dateStr.split('T')[0];
}
}
Object.assign(form, violationDataCopy);
console.log('违纪记录:表单数据已更新:', form);
// 如果有违纪类型ID,触发违纪类型变化事件以设置扣分范围
if (form.disciplineTypeId) {
// 使用 nextTick 确保违纪类型列表已加载
nextTick(() => {
handleDisciplineTypeChange(form.disciplineTypeId);
});
}
// 如果是编辑模式,处理附件数据
if (props.violationData.attachmentOssIds) {
@@ -680,24 +715,56 @@ const handleConfirm = async () => {
// 关闭弹窗
const handleClose = () => {
emit('close');
resetForm();
// 只重置违纪相关字段,保留学生基本信息
Object.assign(form, {
disciplineTypeId: '',
disciplineTypeName: '',
disciplineDate: new Date().toISOString().split('T')[0],
disciplineReason: '',
disciplineContent: '',
scoreDeduction: 0,
attachmentOssIds: ''
});
attachmentList.value = [];
currentTypeMinScore.value = 0;
currentTypeMaxScore.value = 100;
};
// 重置表单
const resetForm = () => {
// 保留学生基本信息(如果有的话)
const preserveStudentInfo = props.studentInfo
? {
studentId: props.studentInfo.studentId || '',
studentNo: props.studentInfo.studentNo || '',
studentName: props.studentInfo.studentName || '',
collegeId: props.studentInfo.collegeId || '',
collegeName: props.studentInfo.collegeName || '',
majorId: props.studentInfo.majorId || '',
majorName: props.studentInfo.majorName || '',
classId: props.studentInfo.classId || '',
className: props.studentInfo.className || '',
bedCode: props.studentInfo.bedCode || '' // 保留床位信息
}
: {
studentId: '',
studentNo: '',
studentName: '',
collegeId: '',
collegeName: '',
majorId: '',
majorName: '',
classId: '',
className: '',
bedCode: ''
};
Object.assign(form, {
studentId: '',
studentNo: '',
studentName: '',
collegeId: '',
collegeName: '',
majorId: '',
majorName: '',
classId: '',
className: '',
...preserveStudentInfo,
disciplineTypeId: '',
disciplineTypeName: '',
disciplineDate: '',
disciplineDate: new Date().toISOString().split('T')[0], // 默认今天
disciplineReason: '',
disciplineContent: '',
scoreDeduction: 0,
@@ -730,6 +797,19 @@ watch(
}
);
// 监听违纪数据变化(新增)
watch(
() => props.violationData,
(newViolationData) => {
// 当违纪数据发生变化时,重新初始化表单
if (newViolationData && props.modelValue) {
console.log('违纪记录:监听到violationData变化,重新初始化表单:', newViolationData);
initForm();
}
},
{ immediate: true, deep: true }
);
// 组件挂载
onMounted(() => {
getAllViolationTypes();
@@ -161,15 +161,14 @@
@update:visible="(value) => (mainDialog.visible = value)"
/>
<!-- 单个宿舍房间检查弹窗组件(修改使用) -->
<DormHygieneCheckSingleDialog
:visible="singleDialog.visible"
:title="singleDialog.title"
:room-info="currentEditData?.roomInfo"
:check-info="currentEditData"
@submit="handleSingleDialogSubmit"
@close="handleSingleDialogClose"
@update:visible="(value) => (singleDialog.visible = value)"
<!-- 编辑宿舍卫生检查弹窗组件(修改使用) -->
<DormHygieneCheckEditDialog
:visible="editDialog.visible"
:title="editDialog.title"
:form-data="currentEditData"
@submit="handleEditDialogSubmit"
@close="handleEditDialogClose"
@update:visible="(value) => (editDialog.visible = value)"
/>
</div>
</template>
@@ -179,6 +178,8 @@ import { ref, reactive, toRefs, onMounted, getCurrentInstance, ComponentInternal
import ElMessage from 'element-plus/es/components/message/index';
import ElMessageBox from 'element-plus/es/components/message-box/index';
import ElLoading from 'element-plus/es/components/loading/index';
// @ts-expect-error: request util import
import request from '@/utils/request';
import {
listDormHygieneCheck,
getDormHygieneCheck,
@@ -194,7 +195,7 @@ import {
DormHygieneCheckCompleteForm
} from '@/api/xg/xgss/dormHygieneCheck/dormHygieneCheck/types';
import DormHygieneCheckMainDialog from './DormHygieneCheckMainDialog.vue';
import DormHygieneCheckSingleDialog from './DormHygieneCheckSingleDialog.vue';
import DormHygieneCheckEditDialog from './DormHygieneCheckEditDialog.vue';
// 类型定义
interface DialogOption {
@@ -229,8 +230,8 @@ const mainDialog = reactive<DialogOption>({
title: ''
});
// 单个房间检查弹窗(修改)
const singleDialog = reactive<DialogOption>({
// 编辑宿舍卫生检查弹窗(修改)
const editDialog = reactive<DialogOption>({
visible: false,
title: ''
});
@@ -326,9 +327,9 @@ const handleMainDialogClose = () => {
reset();
};
/** 单个房间检查弹窗关闭事件处理 */
const handleSingleDialogClose = () => {
singleDialog.visible = false;
/** 编辑弹窗关闭事件处理 */
const handleEditDialogClose = () => {
editDialog.visible = false;
currentEditData.value = null;
};
@@ -346,13 +347,17 @@ const handleMainDialogSubmit = async (formData: any) => {
}
};
/** 单个房间检查弹窗提交事件处理 */
const handleSingleDialogSubmit = async (formData: any) => {
/** 编辑弹窗提交事件处理 */
const handleEditDialogSubmit = async (formData: any) => {
try {
// 单个房间检查弹窗提交的是修改数据
await updateDormHygieneCheck(formData);
// 编辑弹窗提交的是修改数据,调用完整更新接口
await request({
url: '/dormHygieneCheck/dormHygieneCheck/updateCompleteData',
method: 'put',
data: formData
});
ElMessage.success('操作成功');
singleDialog.visible = false;
editDialog.visible = false;
await getList();
} catch (error) {
console.error('提交失败:', error);
@@ -400,8 +405,8 @@ const handleAdd = () => {
/** 修改按钮操作 */
const handleUpdate = async (row?: DormHygieneCheckVO) => {
singleDialog.visible = true;
singleDialog.title = '修改宿舍卫生检查';
editDialog.visible = true;
editDialog.title = '修改宿舍卫生检查';
const loadingInstance = ElLoading.service({
target: '.el-dialog__body',
@@ -416,7 +421,7 @@ const handleUpdate = async (row?: DormHygieneCheckVO) => {
} catch (error) {
console.error('加载编辑数据失败:', error);
ElMessage.error('加载数据失败,请重试');
singleDialog.visible = false;
editDialog.visible = false;
} finally {
loadingInstance.close();
}