提交代码

This commit is contained in:
zlwu committed 2025-08-21 16:24:34 +08:00
1 parent 64a0c9e81a
commit 5022fafc37
115 files changed
+16832 -25

No files matched your search

@@ -0,0 +1,148 @@
# 学生考核审核功能说明
## 📋 功能概述
学生考核审核功能允许班主任查看和管理自己班级学生的考核申请,确保班主任只能查看已提交的申请,不能查看草稿状态的申请。
## 🔐 权限控制
### 班主任查看权限
- **权限标识**: `assessment:approval:teacherClass`
- **数据范围**: 仅限当前登录用户作为班主任的班级
- **状态限制**: 只能查看已提交、班主任审核、已完成状态的申请
### 考核计划查看权限
- **权限标识**: `assessment:approval:available`
- **数据范围**: 所有进行中和已结束的考核计划
## 📊 数据过滤规则
### 班主任班级申请列表查询
#### 接口信息
- **URL**: `GET /assessment/approval/teacherClass`
- **权限**: `assessment:approval:teacherClass`
#### 查询条件
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| scheduleId | Long | 否 | 考核计划ID |
| studentNo | String | 否 | 学号(支持模糊查询) |
| studentName | String | 否 | 学生姓名(支持模糊查询) |
| status | String | 否 | 申请状态(2-已提交 3-班主任审核 6-已完成) |
| pageNum | Integer | 否 | 页码,默认1 |
| pageSize | Integer | 否 | 每页大小,默认10 |
#### 强制过滤条件
```sql
-- 班主任只能查看自己班级的申请
AND a.class_id IN (
SELECT id FROM xg_class_msg
WHERE class_teacher_id = #{teacherId}
AND deleted = 0 AND class_status = '1'
)
-- 排除草稿状态,只查看已提交、班主任审核、已完成的申请
AND a.status IN ('2', '3', '6')
```
#### 状态说明
| 状态码 | 状态名称 | 说明 |
|--------|----------|------|
| 1 | 草稿 | ❌ **班主任不可见** |
| 2 | 已提交 | ✅ 班主任可见 |
| 3 | 班主任审核 | ✅ 班主任可见 |
| 6 | 已完成 | ✅ 班主任可见 |
### 可用考核计划查询
#### 接口信息
- **URL**: `GET /assessment/approval/available`
- **权限**: `assessment:approval:available`
#### 查询条件
```sql
-- 只查询进行中和已结束的计划
WHERE deleted = 0
AND status IN ('2', '3')
ORDER BY start_time DESC
```
#### 计划状态说明
| 状态码 | 状态名称 | 说明 |
|--------|----------|------|
| 1 | 未开始 | ❌ 不可用 |
| 2 | 进行中 | ✅ 可用 |
| 3 | 已结束 | ✅ 可用 |
## 🔍 业务逻辑
### 班主任查看流程
1. **身份验证**: 获取当前登录用户ID
2. **班级查询**: 查询该用户作为班主任的班级列表
3. **申请过滤**: 只查询这些班级下状态为已提交、班主任审核、已完成的申请
4. **数据返回**: 返回过滤后的申请列表
### 数据安全保证
- **权限隔离**: 班主任只能查看自己班级的数据
- **状态过滤**: 自动排除草稿状态的申请
- **数据完整性**: 包含完整的申请信息、状态名称、审核人信息等
## 📝 使用示例
### 基本查询
```bash
GET /assessment/approval/teacherClass
```
### 按状态筛选
```bash
GET /assessment/approval/teacherClass?status=2
```
### 按学生姓名搜索
```bash
GET /assessment/approval/teacherClass?studentName=张三
```
### 分页查询
```bash
GET /assessment/approval/teacherClass?pageNum=1&pageSize=10
```
## 🧪 测试验证
### 测试脚本
使用 `test/学生考核审核API测试脚本.py` 进行功能验证:
```bash
cd test
python 学生考核审核API测试脚本.py
```
### 测试要点
1. ✅ 验证班主任只能查看已提交、班主任审核、已完成状态的申请
2. ✅ 验证草稿状态的申请不会被班主任看到
3. ✅ 验证分页查询功能正常
4. ✅ 验证筛选条件功能正常
5. ✅ 验证只返回班主任自己班级的数据
## ⚠️ 注意事项
1. **草稿状态保护**: 班主任无法查看学生草稿状态的申请,保护学生隐私
2. **权限验证**: 所有接口都需要相应的权限标识
3. **数据一致性**: 确保返回的数据状态与业务规则一致
4. **性能优化**: 使用索引优化查询性能,避免全表扫描
## 📚 相关文档
- [学生考核管理系统API文档](./API/学生考核管理系统API.md)
- [API文档基础规范](./API/API文档基础规范.md)
- [后端开发规范](../.cursor/cursorrules-backend.md)
---
**文档版本**: v1.0.0
**创建时间**: 2025-01-03
**最后更新**: 2025-01-03
**维护人员**: Cursor AI
@@ -0,0 +1,325 @@
# 学生考核申请指标数据获取功能开发说明
## 📋 功能概述
**功能名称**: 学生考核申请指标数据动态获取
**开发时间**: 2025-01-03
**模块**: 学工管理-学生考核管理
**功能描述**: 当学生新增考核申请时,系统根据考核计划ID自动获取关联的指标数据,填入到指标模块中
---
## 🎯 业务需求
### 原始问题
- 学生考核申请表单中的指标模块数据是硬编码的
- 无法根据不同的考核计划动态显示对应的指标
- 指标数据应该来源于考核方案关联指标体系表
### 解决方案
- 根据考核计划ID → 获取考核方案ID → 查询考核方案关联指标体系表
- 动态获取指标数据并填入表单
- 支持不同考核计划显示不同的指标组合
---
## 🏗️ 技术实现
### 1. 数据库关联关系
```
xskh_assessment_schedule (考核计划表)
↓ plan_id
xskh_assessment_plan (考核方案表)
↓ plan_id
xskh_assessment_plan_indicator (考核方案关联指标体系表)
↓ indicator_id
xskh_assessment_indicator (考核指标体系表)
```
### 2. 前端实现
#### 2.1 API接口扩展
**文件**: `web/src/api/xskh/assessmentApplication/assessmentApplication.ts`
```typescript
// 根据考核计划ID获取关联的指标数据
export function getIndicatorsByScheduleId(scheduleId: string | number) {
return request({
url: `/assessment/application/indicators/${scheduleId}`,
method: 'get'
});
}
```
#### 2.2 组件逻辑修改
**文件**: `web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue`
**主要修改**:
1. 导入新的API接口
2. 添加`loadIndicatorsByScheduleId`方法
3. 修改`openDialog`方法,新增时调用指标数据获取
4. 修复接口类型定义,添加`fileList`属性
**核心方法**:
```typescript
/** 根据考核计划ID获取关联的指标数据 */
const loadIndicatorsByScheduleId = async (scheduleId: string | number) => {
try {
const response = await getIndicatorsByScheduleId(scheduleId);
if (response.code === 200 && response.data) {
// 将指标数据转换为自评得分列表格式
form.value.selfScoreList = response.data.map((item: any) => ({
id: undefined,
applicationId: undefined,
indicatorId: item.indicatorId,
indicatorName: item.indicatorName,
indicatorCode: item.indicatorCode,
indicatorDescription: item.description,
indicatorMaxScore: item.maxScore,
planMaxScore: item.maxScore, // 使用方案中设置的最高分值
sortOrder: item.sortOrder,
selfScore: 0,
selfRemark: '',
teacherScore: 0,
teacherRemark: ''
}));
}
} catch (error) {
console.error('获取指标数据失败:', error);
form.value.selfScoreList = [];
}
};
```
### 3. 后端实现
#### 3.1 Controller层
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/controller/XskhAssessmentApplicationApprovalController.java`
```java
/**
* 根据考核计划ID获取关联的指标数据
*/
@SaCheckPermission("assessment:application:query")
@GetMapping("/indicators/{scheduleId}")
public R<List<Object>> getIndicatorsByScheduleId(
@NotNull(message = "考核计划ID不能为空") @PathVariable Long scheduleId) {
return R.ok(xskhAssessmentApplicationApprovalService.getIndicatorsByScheduleId(scheduleId));
}
```
#### 3.2 Service层
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/IXskhAssessmentApplicationApprovalService.java`
```java
/**
* 根据考核计划ID获取关联的指标数据
*/
List<Object> getIndicatorsByScheduleId(Long scheduleId);
```
**实现类**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentApplicationApprovalServiceImpl.java`
```java
/**
* 根据考核计划ID获取关联的指标数据
*/
@Override
public List<Object> getIndicatorsByScheduleId(Long scheduleId) {
// 1. 根据考核计划ID获取考核方案ID
XskhAssessmentScheduleVo schedule = assessmentScheduleService.queryById(scheduleId);
if (schedule == null) {
return new ArrayList<>();
}
Long planId = schedule.getPlanId();
if (planId == null) {
return new ArrayList<>();
}
// 2. 根据考核方案ID查询关联的指标数据
List<Object> indicators = baseMapper.selectIndicatorsByPlanId(planId);
return indicators;
}
```
#### 3.3 Mapper层
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/mapper/XskhAssessmentApplicationApprovalMapper.java`
```java
/**
* 根据考核方案ID查询关联的指标数据
*/
List<Object> selectIndicatorsByPlanId(Long planId);
```
**XML文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentApplicationApprovalMapper.xml`
```xml
<!-- 根据考核方案ID查询关联的指标数据 -->
<select id="selectIndicatorsByPlanId" parameterType="Long" resultType="java.util.Map">
SELECT
i.id as indicatorId,
i.indicator_code as indicatorCode,
i.indicator_name as indicatorName,
i.description,
pi.max_score as maxScore,
pi.weight,
pi.sort_order as sortOrder
FROM xskh_assessment_plan_indicator pi
INNER JOIN xskh_assessment_indicator i ON pi.indicator_id = i.id
WHERE pi.plan_id = #{planId}
AND pi.deleted = 0
AND i.deleted = 0
ORDER BY pi.sort_order ASC, i.sort_order ASC
</select>
```
---
## 🔄 业务流程
### 1. 学生新增考核申请流程
```
1. 学生选择考核计划
2. 系统根据考核计划ID获取考核方案ID
3. 根据考核方案ID查询关联的指标数据
4. 将指标数据转换为表单格式
5. 填入指标模块表格中
6. 学生可以进行自评打分
```
### 2. 数据流转过程
```
前端选择考核计划 → 调用API接口 → 后端查询数据库 → 返回指标数据 → 前端渲染表格
```
---
## 📊 数据结构
### 返回的指标数据结构
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"indicatorId": 1,
"indicatorCode": "ZD001",
"indicatorName": "思想道德",
"description": "思想道德表现",
"maxScore": 20.00,
"weight": 1.00,
"sortOrder": 1
},
{
"indicatorId": 2,
"indicatorCode": "ZD002",
"indicatorName": "学习表现",
"description": "学习表现情况",
"maxScore": 30.00,
"weight": 1.00,
"sortOrder": 2
}
]
}
```
### 转换后的表单数据结构
```typescript
{
id: undefined,
applicationId: undefined,
indicatorId: 1,
indicatorName: "思想道德",
indicatorCode: "ZD001",
indicatorDescription: "思想道德表现",
indicatorMaxScore: 20,
planMaxScore: 20,
sortOrder: 1,
selfScore: 0,
selfRemark: "",
teacherScore: 0,
teacherRemark: ""
}
```
---
## 🔐 权限配置
### API权限
- **权限字符**: `assessment:application:query`
- **说明**: 查询考核申请相关数据的权限
- **应用**: 获取指标数据接口
### 菜单权限
- 无需额外配置,复用现有的考核申请查询权限
---
## 🧪 测试验证
### 1. 功能测试
- [ ] 选择不同的考核计划,验证指标数据是否正确获取
- [ ] 验证指标数据的完整性和准确性
- [ ] 验证排序是否正确
- [ ] 验证最高分值是否正确显示
### 2. 异常测试
- [ ] 考核计划不存在的情况
- [ ] 考核方案未关联指标的情况
- [ ] 网络异常的情况
- [ ] 权限不足的情况
### 3. 性能测试
- [ ] 大量指标数据的加载性能
- [ ] 并发访问的性能表现
---
## 📝 注意事项
### 1. 开发注意事项
- 确保数据库表结构正确
- 注意逻辑删除字段的处理
- 保持数据一致性
- 做好异常处理
### 2. 部署注意事项
- 确保数据库脚本已执行
- 检查权限配置是否正确
- 验证API接口是否可访问
### 3. 维护注意事项
- 定期检查指标数据的完整性
- 监控API接口的响应时间
- 及时处理异常情况
---
## 🚀 后续优化
### 1. 功能优化
- 支持指标数据的缓存机制
- 添加指标数据的版本控制
- 支持指标数据的批量导入导出
### 2. 性能优化
- 优化SQL查询性能
- 添加数据库索引
- 实现数据分页加载
### 3. 用户体验优化
- 添加加载状态提示
- 优化错误提示信息
- 支持指标数据的搜索和筛选
---
**📅 开发完成时间**: 2025-01-03
**👨‍💻 开发人员**: 系统开发团队
**📋 文档版本**: v1.0.0
@@ -0,0 +1,189 @@
# 学生考核申请附件功能改造说明
## 改造概述
本次改造将学生考核申请表单中的附件上传功能升级为与宿舍违纪模块相同的现代化附件管理系统,提供更好的用户体验和功能完整性。
## 改造内容
### 1. 界面升级
- **拖拽上传**: 支持拖拽文件到上传区域
- **卡片式展示**: 文件以卡片形式展示,每行5个文件
- **缩略图预览**: 图片文件显示缩略图,其他文件显示文件类型图标
- **文件名显示**: 智能截断长文件名,保留后缀
- **操作按钮**: 每个文件卡片提供下载和删除按钮
### 2. 功能增强
- **多文件上传**: 支持同时上传多个文件,最多15个
- **文件类型限制**: 支持图片、视频、PDF、Word文档等格式
- **文件大小限制**: 单个文件不超过10MB
- **实时预览**: 图片文件可直接预览
- **文件下载**: 支持下载已上传的文件
- **文件删除**: 支持删除不需要的文件
### 3. 技术实现
#### 3.1 上传配置
```typescript
// 上传地址
const uploadAction = computed(() => {
return import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload';
});
// 上传请求头
const uploadHeaders = computed(() => {
return {
'Authorization': `Bearer ${getToken() || ''}`,
'clientid': import.meta.env.VITE_APP_CLIENT_ID || 'e5cd7e4891bf95d1d19206ce24a7b32e'
};
});
// 业务类型标识
const uploadData = computed(() => {
return {
bizType: 'assessment_bonus_attachment'
};
});
```
#### 3.2 文件处理函数
- `handleBeforeUpload`: 上传前文件校验
- `handleUploadSuccess`: 上传成功处理
- `handleUploadError`: 上传失败处理
- `handleFileChange`: 文件列表变更处理
- `handleFilePreview`: 文件预览处理
- `handleDownloadAttachment`: 文件下载处理
- `handleRemoveAttachmentConfirm`: 文件删除确认
- `handleRemoveAttachment`: 文件删除处理
#### 3.3 辅助函数
- `isImageFile`: 判断是否为图片文件
- `getFileOssId`: 获取文件OSS ID
- `getFileExt`: 获取文件扩展名
- `getShortFileName`: 获取缩写的文件名
### 4. 数据结构更新
#### 4.1 接口定义
```typescript
interface XskhAssessmentBonusApprovalForm {
id?: string | number;
applicationId?: string | number;
bonusItemName?: string;
bonusContent?: string;
bonusScore?: number;
attachment?: string; // 存储OSS ID列表,逗号分隔
approvalStatus?: string;
approvalScore?: number;
approvalBy?: string | number;
approvalTime?: string;
approvalRemark?: string;
fileList?: any[]; // 新增:存储文件列表,用于界面展示
}
```
#### 4.2 数据转换
- **保存时**: 将`fileList`中的`ossId`合并为`attachment`字段
- **加载时**: 将`attachment`字段解析为`fileList`用于界面展示
### 5. 样式设计
#### 5.1 容器样式
- 固定高度,支持滚动
- 灰色背景,圆角边框
- 响应式布局
#### 5.2 文件卡片样式
- 180px高度,圆角设计
- 悬停效果,阴影变化
- 缩略图区域110px高度
- 文件名区域32px高度
- 操作按钮区域38px高度
#### 5.3 网格布局
- 使用CSS Grid布局
- 每行5个文件卡片
- 9px间距
- 自适应宽度
## 使用说明
### 1. 新增附加分项目
1. 点击"添加附加分"按钮
2. 填写项目信息
3. 在附件区域拖拽或点击上传文件
4. 文件上传成功后显示在卡片列表中
### 2. 编辑附加分项目
1. 打开编辑弹窗
2. 系统自动加载已有附件
3. 可以添加、删除、下载附件
4. 保存时自动更新附件信息
### 3. 查看考核申请
1. 打开查看弹窗
2. 附件区域为只读状态
3. 可以预览图片和下载文件
4. 不能进行上传和删除操作
## 兼容性说明
### 1. 向后兼容
- 保持原有的`attachment`字段格式
- 支持旧数据的正常加载和显示
- 不影响现有业务逻辑
### 2. 数据迁移
- 无需数据迁移
- 新功能与旧数据格式兼容
- 自动处理数据转换
## 注意事项
### 1. 文件限制
- 支持格式:jpg, jpeg, png, gif, bmp, webp, pdf, doc, docx, txt, mp4, avi, mov
- 文件大小:单个文件不超过10MB
- 文件数量:每个附加分项目最多15个文件
### 2. 权限控制
- 查看模式下禁用上传和删除功能
- 编辑模式下支持所有操作
- 新增模式下支持所有操作
### 3. 错误处理
- 文件格式错误时显示提示
- 文件大小超限时显示提示
- 上传失败时显示错误信息
- 网络异常时提供重试机制
## 技术依赖
### 1. 组件依赖
- `ImagePreview`: 图片预览组件
- `ElUpload`: Element Plus上传组件
- `ElButtonGroup`: 按钮组组件
### 2. 图标依赖
- `UploadFilled`: 上传图标
- `Document`: 文档图标
- `Loading`: 加载图标
- `Download`: 下载图标
- `Delete`: 删除图标
### 3. 工具函数
- `getToken`: 获取认证令牌
- `request`: HTTP请求工具
- `ElMessage`: 消息提示
- `ElMessageBox`: 确认对话框
- `ElLoading`: 加载状态
## 总结
本次改造成功将学生考核申请的附件功能升级为现代化的文件管理系统,提供了:
1. **更好的用户体验**: 拖拽上传、卡片展示、实时预览
2. **完整的功能**: 上传、下载、删除、预览
3. **稳定的技术**: 基于成熟的OSS存储系统
4. **良好的兼容性**: 与现有系统无缝集成
改造后的附件功能与宿舍违纪模块保持一致,提供了统一的用户体验和功能标准。
@@ -0,0 +1,188 @@
# 学生考核计划管理模块完善总结
## 📋 任务概述
根据用户需求,完善学生考核计划管理模块,添加缺失的数据库字段:
- `academic_year` varchar(20) NOT NULL COMMENT '学年'
- `semester` char(1) NOT NULL COMMENT '学期(1-第一学期 2-第二学期)'
- `data_start_time` datetime NOT NULL COMMENT '数据统计开始时间'
- `data_end_time` datetime NOT NULL COMMENT '数据统计结束时间'
## ✅ 完成的工作
### 1. 后端代码完善
#### 1.1 实体类更新
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/XskhAssessmentSchedule.java`
**新增字段**:
- `academicYear` - 学年
- `semester` - 学期
- `dataStartTime` - 数据统计开始时间
- `dataEndTime` - 数据统计结束时间
- `planName` - 考核方案名称
**技术要点**:
- 添加了 `@JsonFormat` 注解用于时间字段格式化
- 保持了与数据库表结构的一致性
#### 1.2 VO类更新
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentScheduleVo.java`
**新增字段**:
- 所有实体类中的新字段
- `semesterName` - 学期名称(用于前端显示)
#### 1.3 BO类更新
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/bo/XskhAssessmentScheduleBo.java`
**新增字段**:
- 所有实体类中的新字段
- 添加了相应的验证注解(`@NotBlank`、`@NotNull`)
#### 1.4 Mapper XML更新
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentScheduleMapper.xml`
**更新内容**:
- 在 `resultMap` 中添加了新字段的映射关系
- 确保 MyBatis 能正确映射所有字段
#### 1.5 Service实现更新
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentScheduleServiceImpl.java`
**更新内容**:
- 在 `buildQueryWrapper` 方法中添加了对新字段的查询条件支持
- 支持按学年、学期进行模糊查询和精确查询
### 2. 前端代码完善
#### 2.1 类型定义更新
**文件**: `web/src/api/xskh/assessmentSchedule/types.ts`
**新增字段**:
- 在所有接口类型中添加了新字段
- 更新了查询条件接口,支持按新字段查询
#### 2.2 列表页面更新
**文件**: `web/src/views/xskh/assessmentSchedule/index.vue`
**新增功能**:
- 搜索条件:添加学年、学期搜索
- 表格列:添加学年、学期、数据统计时间列
- 字典支持:使用 `student_semester` 字典显示学期名称
#### 2.3 表单页面更新
**文件**: `web/src/views/xskh/assessmentSchedule/AssessmentScheduleForm.vue`
**新增功能**:
- 表单字段:添加学年、学期、数据统计时间字段
- 验证规则:添加必填验证和时间逻辑验证
- 字典支持:学期选择使用下拉框
### 3. 测试脚本
#### 3.1 API测试脚本
**文件**: `test/学生考核计划API测试脚本.py`
**测试内容**:
- 完整的CRUD操作测试
- 新字段的创建、查询、更新测试
- 不同查询条件的组合测试
- 状态操作测试(上线、下线、完成)
## 🔧 技术要点
### 1. 数据库字段映射
- 确保后端实体类字段名与数据库字段名正确映射
- 使用 `@TableField` 注解处理字段名不一致的情况
### 2. 时间字段处理
- 使用 `@JsonFormat` 注解统一时间格式
- 前端使用 `el-date-picker` 组件处理时间选择
### 3. 字典数据支持
- 学期字段使用 `student_semester` 字典
- 前端通过 `useDict` 获取字典数据
### 4. 查询条件支持
- 学年支持模糊查询
- 学期支持精确查询
- 支持多条件组合查询
### 5. 表单验证
- 必填字段验证
- 时间逻辑验证(结束时间大于开始时间)
- 数据统计时间逻辑验证
## 📊 功能验证
### 1. 列表功能
- ✅ 支持按学年搜索
- ✅ 支持按学期搜索
- ✅ 显示学年、学期、数据统计时间列
- ✅ 学期显示为中文名称
### 2. 表单功能
- ✅ 学年输入框
- ✅ 学期下拉选择
- ✅ 数据统计时间选择器
- ✅ 时间逻辑验证
- ✅ 必填字段验证
### 3. API功能
- ✅ 创建包含新字段的考核计划
- ✅ 查询包含新字段的考核计划
- ✅ 更新包含新字段的考核计划
- ✅ 按新字段进行条件查询
## 🚀 使用说明
### 1. 启动服务
```bash
# 启动后端服务
cd backend
./hot-start.sh
# 启动前端服务
cd web
npm run dev
```
### 2. 运行测试
```bash
# 运行API测试脚本
cd test
python 学生考核计划API测试脚本.py
```
### 3. 功能验证
1. 访问前端页面:`http://localhost`
2. 进入学生考核管理 → 考核计划管理
3. 测试新增、查询、修改功能
4. 验证新字段是否正确显示和保存
## 📝 注意事项
### 1. 数据库要求
- 确保数据库表 `xskh_assessment_schedule` 已包含新字段
- 确保字典表 `sys_dict_data` 中已配置 `student_semester` 字典
### 2. 权限配置
- 确保用户有相应的菜单和按钮权限
- 权限字符格式:`assessment:schedule:*`
### 3. 字典配置
- 学期字典标识:`student_semester`
- 字典值:`1`-第一学期,`2`-第二学期
## 🎯 总结
本次完善工作成功为学生考核计划管理模块添加了以下功能:
1. **完整的字段支持**:学年、学期、数据统计时间
2. **完善的查询功能**:支持多条件组合查询
3. **友好的用户界面**:字典显示、时间选择器
4. **严格的数据验证**:必填验证、时间逻辑验证
5. **完整的测试覆盖**:API测试脚本验证所有功能
所有代码修改都遵循了项目的开发规范,确保了代码质量和功能完整性。
@@ -186,7 +186,7 @@ CREATE TABLE `xskh_assessment_indicator` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-未删除 1-已删除)',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_indicator_code` (`indicator_code`),
KEY `uk_indicator_code` (`indicator_code`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核指标体系表';
@@ -227,12 +227,12 @@ CREATE TABLE `xskh_assessment_plan_indicator` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-未删除 1-已删除)',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_plan_indicator` (`plan_id`, `indicator_id`),
KEY `uk_plan_indicator` (`plan_id`, `indicator_id`),
KEY `idx_plan_id` (`plan_id`),
KEY `idx_indicator_id` (`indicator_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='考核方案关联指标体系表';
-- 9、考核创建一个表关联考核指标跟违纪处分类型和奖励类型的关联表
-- 9、考核指标跟违纪处分类型和奖励类型的关联表
DROP TABLE IF EXISTS `xskh_assessment_indicator_discipline_type`;
CREATE TABLE `xskh_assessment_indicator_discipline_type` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
@@ -247,7 +247,7 @@ CREATE TABLE `xskh_assessment_indicator_discipline_type` (
`deleted` tinyint(1) DEFAULT 0 COMMENT '删除标志(0-未删除 1-已删除)',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='考核指标关联违纪处分类型表';
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='考核指标关联违纪处分类型和奖励类型表';
-- 10. 学生考核计划表
DROP TABLE IF EXISTS `xskh_assessment_schedule`;
@@ -277,9 +277,9 @@ CREATE TABLE `xskh_assessment_schedule` (
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核计划表';
-- 11. 学生考核申请表
DROP TABLE IF EXISTS `xskh_assessment_application`;
CREATE TABLE `xskh_assessment_application` (
-- 11. 学生考核申请、班主任审核表
DROP TABLE IF EXISTS `xskh_assessment_application_approval`;
CREATE TABLE `xskh_assessment_application_approval` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`schedule_id` bigint(20) NOT NULL COMMENT '考核计划ID',
`student_id` bigint(20) NOT NULL COMMENT '学生ID',
@@ -293,6 +293,7 @@ CREATE TABLE `xskh_assessment_application` (
`class_name` varchar(100) NOT NULL COMMENT '班级名称',
`total_score` decimal(5,2) DEFAULT 0.00 COMMENT '总得分',
`self_score` decimal(5,2) DEFAULT 0.00 COMMENT '自评得分',
`self_remark` varchar(500) DEFAULT NULL COMMENT '自评备注',
`teacher_score` decimal(5,2) DEFAULT 0.00 COMMENT '班主任评分',
`status` char(1) DEFAULT '1' COMMENT '状态(1-草稿 2-已提交 3-班主任审核 6-已完成)',
`submit_time` datetime DEFAULT NULL COMMENT '提交时间',
@@ -311,7 +312,7 @@ CREATE TABLE `xskh_assessment_application` (
KEY `idx_schedule_id` (`schedule_id`),
KEY `idx_student_id` (`student_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核申请表';
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核申请、班主任审核表';
-- 12. 学生考核自评、班主任评分表
DROP TABLE IF EXISTS `xskh_assessment_self_score`;
@@ -338,11 +339,11 @@ CREATE TABLE `xskh_assessment_self_score` (
KEY `uk_application_indicator` (`application_id`, `indicator_id`),
KEY `idx_application_id` (`application_id`),
KEY `idx_indicator_id` (`indicator_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核自评得分表';
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核自评、班主任评分表';
-- 13. 学生考核附加分表
DROP TABLE IF EXISTS `xskh_assessment_bonus`;
CREATE TABLE `xskh_assessment_bonus` (
-- 13. 学生附加分申请、班主任审核得分表
DROP TABLE IF EXISTS `xskh_assessment_bonus_approval`;
CREATE TABLE `xskh_assessment_bonus_approval` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`application_id` bigint(20) NOT NULL COMMENT '考核申请ID',
`bonus_name` varchar(100) NOT NULL COMMENT '附加分项目名称',
@@ -365,6 +366,6 @@ CREATE TABLE `xskh_assessment_bonus` (
PRIMARY KEY (`id`),
KEY `idx_application_id` (`application_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生考核附加分表';
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='学生附加分申请、班主任审核得分表';
@@ -0,0 +1,219 @@
# MyBatis-Plus字段自动填充机制分析
## 问题现象
在 `XskhAssessmentIndicatorTypeController.java` 的 `add` 方法中,当注释掉手动设置 `createDept` 的代码后:
- ✅ `create_by`、`create_time`、`update_by`、`update_time` 字段能自动填充
- ❌ `create_dept` 字段无法自动填充,显示为 NULL
## 根本原因分析
### 1. 继承关系分析
```java
// 实体类继承关系
XskhAssessmentIndicatorType extends TenantEntity
↓
TenantEntity extends BaseEntity
↓
BaseEntity (包含所有基础字段)
```
### 2. BaseEntity中的字段定义
```java
public class BaseEntity implements Serializable {
/**
* 创建部门
*/
@TableField(fill = FieldFill.INSERT)
private Long createDept;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
private Long createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/**
* 更新者
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
}
```
### 3. InjectionMetaObjectHandler自动填充逻辑
```java
@Override
public void insertFill(MetaObject metaObject) {
try {
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
// 获取当前时间
Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date());
baseEntity.setCreateTime(current);
baseEntity.setUpdateTime(current);
// 关键逻辑:只有当createBy为空时才填充用户信息
if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
LoginUser loginUser = getLoginUser();
if (ObjectUtil.isNotNull(loginUser)) {
Long userId = loginUser.getUserId();
baseEntity.setCreateBy(userId);
baseEntity.setUpdateBy(userId);
// 这里填充createDept
baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId()));
}
}
}
} catch (Exception e) {
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
}
}
```
## 问题根源
### 关键发现:条件判断逻辑
在 `InjectionMetaObjectHandler.insertFill()` 方法中,有一个关键的条件判断:
```java
if (ObjectUtil.isNull(baseEntity.getCreateBy())) {
// 只有当createBy为空时,才会执行用户信息填充
// 包括:createBy、updateBy、createDept
}
```
### 问题分析
1. **前端传入了createBy值**:
```json
{
"typeCode": "1",
"typeName": "1",
"description": "1",
"status": "1",
"sortOrder": 0,
"createBy": 1, // ← 前端传入了createBy
"createTime": "2025-08-20 18:52:43",
"updateBy": 1, // ← 前端传入了updateBy
"updateTime": "2025-08-20 18:52:43"
}
```
2. **条件判断失败**:
- `baseEntity.getCreateBy()` 不为空(值为1)
- 因此 `ObjectUtil.isNull(baseEntity.getCreateBy())` 返回 `false`
- 整个用户信息填充逻辑被跳过
- 包括 `createDept` 的自动填充也被跳过
3. **其他字段为什么能填充**:
- `createTime` 和 `updateTime`:在条件判断**之前**就被设置了
- `createBy` 和 `updateBy`:前端已经传入,不需要自动填充
- `createDept`:依赖条件判断内的逻辑,被跳过了
## 解决方案对比
### 方案1:移除前端传入的基础字段(推荐)
**优点**:
- 完全依赖框架自动填充
- 代码简洁,无需手动设置
- 符合框架设计理念
**实现**:
```java
// 前端不再传入基础字段
{
"typeCode": "1",
"typeName": "1",
"description": "1",
"status": "1",
"sortOrder": 0
// 移除:createBy, createTime, updateBy, updateTime
}
```
### 方案2:手动设置createDept(当前方案)
**优点**:
- 不依赖前端修改
- 确保字段有值
**缺点**:
- 需要额外的代码逻辑
- 可能与其他自动填充逻辑冲突
**实现**:
```java
// 在Service中手动设置
if (add.getCreateDept() == null && deptId != null) {
add.setCreateDept(deptId);
}
```
### 方案3:修改InjectionMetaObjectHandler逻辑
**优点**:
- 从根本上解决问题
- 对所有模块生效
**缺点**:
- 影响全局逻辑
- 可能影响其他模块
**实现**:
```java
// 修改条件判断逻辑
if (ObjectUtil.isNull(baseEntity.getCreateBy()) || ObjectUtil.isNull(baseEntity.getCreateDept())) {
// 当createBy或createDept任一为空时,都进行填充
}
```
## 最佳实践建议
### 1. 前端规范
- **不要传入基础字段**:`createBy`、`createTime`、`updateBy`、`updateTime`、`createDept`
- 让框架完全负责这些字段的自动填充
### 2. 后端规范
- **实体类必须继承BaseEntity或TenantEntity**
- **BO类必须包含基础字段**(用于数据传输)
- **VO类必须包含基础字段**(用于数据展示)
### 3. 代码模板
```java
// 标准的新增方法模板
@Override
public Boolean insertByBo(XxxBo bo) {
Xxx add = MapstructUtils.convert(bo, Xxx.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
}
return flag;
}
```
## 总结
问题的根本原因是:**前端传入了 `createBy` 字段,导致框架的自动填充条件判断失败,从而跳过了 `createDept` 的自动填充**。
**推荐解决方案**:移除前端传入的基础字段,让框架完全负责自动填充,这样既简洁又符合框架设计理念。
@@ -0,0 +1,211 @@
# 学生考核指标indicator_type_name字段问题修复
## 问题描述
在新增学生考核指标时,出现数据库插入错误:
```
Field 'indicator_type_name' doesn't have a default value
```
## 问题分析
### 1. 根本原因
- 数据库表 `xskh_assessment_indicator` 中的 `indicator_type_name` 字段设置为 `NOT NULL`
- 前端传递的数据中只有 `indicatorTypeId`,没有 `indicatorTypeName`
- 后端Service层在插入数据时没有自动设置 `indicatorTypeName` 字段的值
### 2. 字段映射问题
- 前端使用 `indicatorDescription` 字段,但后端数据库字段是 `description`
- 为了保持前后端代码和数据库字段的一致性,统一使用 `description` 字段名
## 解决方案
### 1. 后端Service层修复
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentIndicatorServiceImpl.java`
#### 1.1 添加依赖注入
```java
private final XskhAssessmentIndicatorTypeMapper indicatorTypeMapper;
```
#### 1.2 修改insertByBo方法
```java
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(XskhAssessmentIndicatorBo bo) {
XskhAssessmentIndicator add = MapstructUtils.convert(bo, XskhAssessmentIndicator.class);
// 根据指标类型ID设置指标类型名称
if (add.getIndicatorTypeId() != null) {
XskhAssessmentIndicatorType indicatorType = indicatorTypeMapper.selectById(add.getIndicatorTypeId());
if (indicatorType != null) {
add.setIndicatorTypeName(indicatorType.getTypeName());
}
}
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setId(add.getId());
// 保存关联关系
saveIndicatorRelations(add.getId(), bo.getDisciplineTypeIds(), bo.getRewardTypeIds());
}
return flag;
}
```
#### 1.3 修改updateByBo方法
```java
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(XskhAssessmentIndicatorBo bo) {
XskhAssessmentIndicator update = MapstructUtils.convert(bo, XskhAssessmentIndicator.class);
// 根据指标类型ID设置指标类型名称
if (update.getIndicatorTypeId() != null) {
XskhAssessmentIndicatorType indicatorType = indicatorTypeMapper.selectById(update.getIndicatorTypeId());
if (indicatorType != null) {
update.setIndicatorTypeName(indicatorType.getTypeName());
}
}
validEntityBeforeSave(update);
boolean flag = baseMapper.updateById(update) > 0;
if (flag) {
// 更新关联关系
saveIndicatorRelations(update.getId(), bo.getDisciplineTypeIds(), bo.getRewardTypeIds());
}
return flag;
}
```
### 2. 字段映射修复
为了保持前后端代码和数据库字段的一致性,统一使用 `description` 字段名:
#### 2.1 前端类型定义修复
**文件**: `web/src/api/xskh/assessmentIndicator/types.ts`
将所有接口中的 `indicatorDescription` 字段改为 `description`:
- `XskhAssessmentIndicatorBo`
- `XskhAssessmentIndicatorVo`
- `XskhAssessmentIndicatorForm`
#### 2.2 前端表单修复
**文件**: `web/src/views/xskh/assessmentIndicator/AssessmentIndicatorForm.vue`
- 表单字段名:`indicatorDescription` → `description`
- 表单验证规则:`indicatorDescription` → `description`
- 初始数据:`indicatorDescription` → `description`
### 3. Mapper XML修复
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentIndicatorMapper.xml`
添加缺失的 `createDept` 字段映射:
```xml
<result property="createDept" column="create_dept"/>
```
### 4. 前端CSS修复
**文件**: `web/src/views/xskh/assessmentIndicator/AssessmentIndicatorForm.vue`
修复CSS语法错误:
```css
/* 固定多选框高度,防止动态变化 */
.fixed-height-select :deep(.el-select__tags) {
max-height: 32px;
overflow: hidden;
}
.fixed-height-select :deep(.el-select__tags-text) {
max-width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.fixed-height-select :deep(.el-select__collapse-tags) {
max-width: 100%;
}
```
## 测试验证
### 1. 创建测试脚本
**文件**: `test/学生考核指标API测试脚本.py`
包含以下测试用例:
- 登录认证
- 新增学生考核指标
- 查询指标列表
- 查询指标详情
- 修改指标
### 2. 测试数据
```json
{
"indicatorCode": "zbbm01",
"indicatorName": "学生指标01",
"description": "学生指标01指标说明",
"indicatorTypeId": "1958131304973221889",
"maxScore": 2.1,
"status": "0",
"sortOrder": 0,
"disciplineTypeIds": [
"1958135628856049666",
"1958135753561096193"
],
"rewardTypeIds": [
"1958135350199074817",
"1958135400207761409"
]
}
```
## 修复效果
### 1. 问题解决
- ✅ 解决了 `indicator_type_name` 字段缺失默认值的问题
- ✅ 统一了前后端字段命名,使用 `description` 字段
- ✅ 修复了CSS语法错误
- ✅ 完善了Mapper XML字段映射
### 2. 功能增强
- ✅ 自动根据指标类型ID设置指标类型名称
- ✅ 统一前后端字段命名,提高代码一致性
- ✅ 完善了错误处理和日志记录
### 3. 代码质量
- ✅ 遵循项目开发规范
- ✅ 添加了完整的注释说明
- ✅ 提供了测试脚本验证功能
## 注意事项
1. **数据库字段约束**: 确保数据库表设计合理,避免不必要的NOT NULL约束
2. **字段命名一致性**: 前后端字段名应保持一致,避免不必要的映射转换
3. **自动填充**: 对于关联字段,应该在Service层自动填充,而不是依赖前端传递
4. **测试验证**: 每次修复后都应该进行完整的测试验证
## 相关文件
- `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentIndicatorServiceImpl.java`
- `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/bo/XskhAssessmentIndicatorBo.java`
- `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/XskhAssessmentIndicator.java`
- `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentIndicatorVo.java`
- `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentIndicatorMapper.xml`
- `web/src/api/xskh/assessmentIndicator/types.ts`
- `web/src/views/xskh/assessmentIndicator/AssessmentIndicatorForm.vue`
- `test/学生考核指标API测试脚本.py`
---
**修复时间**: 2025-01-03
**修复人员**: AI Assistant
**验证状态**: 待验证
@@ -0,0 +1,162 @@
# 学生考核指标类型createDept字段问题修复
## 问题描述
在 `XskhAssessmentIndicatorTypeController.java` 类的 `add` 方法执行后,数据库中查看数据时发现 `create_dept` 字段(创建人的部门ID)没有插入数据,显示为 NULL。
## 问题分析
通过代码分析,发现问题的根本原因是:
### 1. VO类缺少createDept字段
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentIndicatorTypeVo.java`
**问题**: VO类中缺少 `createDept` 字段定义,导致字段映射不完整。
### 2. BO类缺少createDept字段
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/bo/XskhAssessmentIndicatorTypeBo.java`
**问题**: BO类中缺少 `createDept` 字段定义,影响数据传输。
### 3. Mapper XML缺少字段映射
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentIndicatorTypeMapper.xml`
**问题**: resultMap中缺少 `createDept` 字段的映射配置,导致数据库字段无法正确映射到实体属性。
## 解决方案
### 1. 修复VO类
在 `XskhAssessmentIndicatorTypeVo.java` 中添加 `createDept` 字段:
```java
/**
* 创建部门
*/
private Long createDept;
```
### 2. 修复BO类
在 `XskhAssessmentIndicatorTypeBo.java` 中添加基础字段:
```java
/**
* 创建部门
*/
private Long createDept;
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
private Date updateTime;
```
### 3. 修复Mapper XML
在 `XskhAssessmentIndicatorTypeMapper.xml` 的两个resultMap中添加字段映射:
```xml
<result property="createDept" column="create_dept"/>
```
### 4. 增强Service实现
在 `XskhAssessmentIndicatorTypeServiceImpl.java` 中添加调试日志和手动设置逻辑:
```java
// 调试日志:检查当前登录用户信息
try {
Long userId = LoginHelper.getUserId();
Long deptId = LoginHelper.getDeptId();
log.info("🔍 调试信息 - 当前登录用户ID: {}, 部门ID: {}", userId, deptId);
// 手动设置createDept字段,确保有值
if (add.getCreateDept() == null && deptId != null) {
add.setCreateDept(deptId);
log.info("✅ 手动设置createDept字段: {}", deptId);
} else {
log.info("⚠️ createDept字段状态: {}", add.getCreateDept());
}
} catch (Exception e) {
log.warn("⚠️ 获取登录用户信息失败: {}", e.getMessage());
}
```
## 技术原理
### MyBatis-Plus字段填充机制
系统使用 MyBatis-Plus 的 `@TableField(fill = FieldFill.INSERT)` 注解来自动填充字段:
```java
@TableField(fill = FieldFill.INSERT)
private Long createDept;
```
### 字段填充处理器
`InjectionMetaObjectHandler` 负责在插入时自动填充字段:
```java
baseEntity.setCreateDept(ObjectUtils.notNull(baseEntity.getCreateDept(), loginUser.getDeptId()));
```
### 字段映射关系
- 数据库字段: `create_dept`
- 实体属性: `createDept`
- 映射配置: `<result property="createDept" column="create_dept"/>`
## 验证方法
### 1. 创建测试脚本
创建 `test/学生考核指标类型API测试脚本.py` 来验证修复效果。
### 2. 测试步骤
1. 登录系统获取token
2. 调用新增API创建测试数据
3. 调用查询API检查createDept字段
4. 验证字段是否正确填充
### 3. 预期结果
- createDept字段应该包含当前登录用户的部门ID
- 不再显示为NULL
## 预防措施
### 1. 规范检查
- 所有VO类必须包含四个基础字段:`createBy`、`createTime`、`updateBy`、`updateTime`
- 新增 `createDept` 字段作为第五个基础字段
- Mapper XML必须包含完整的字段映射
### 2. 代码模板
创建标准的代码模板,确保新开发的模块不会遗漏基础字段。
### 3. 自动化测试
建立自动化测试机制,定期检查字段映射的完整性。
## 相关文件
### 修改的文件
1. `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentIndicatorTypeVo.java`
2. `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/bo/XskhAssessmentIndicatorTypeBo.java`
3. `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentIndicatorTypeMapper.xml`
4. `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentIndicatorTypeServiceImpl.java`
### 新增的文件
1. `test/学生考核指标类型API测试脚本.py`
## 总结
通过修复VO类、BO类、Mapper XML的字段映射问题,并增强Service实现的调试功能,成功解决了 `create_dept` 字段不填充的问题。这个问题的根本原因是字段映射不完整,导致MyBatis-Plus的自动填充机制无法正常工作。
修复后的系统能够正确记录创建人的部门信息,为后续的数据分析和权限控制提供了基础支持。
@@ -0,0 +1,218 @@
# 学生考核时间格式修复报告
## 📋 问题描述
### 问题现象
在学生考核管理系统中,点击保存按钮调用接口 `http://localhost/dev-api/assessment/approval/approve` 时,返回的数据中 `bonusApprovalList` 和 `bonusList` 的时间字段显示为不标准的格式:
```json
{
"bonusApprovalList": [
{
"createTime": "2025/8/21 上午10:52",
"updateTime": "2025/8/21 上午10:52"
}
]
}
```
### 问题分析
经过代码分析,发现问题出现在以下两个VO类文件中:
1. **XskhAssessmentBonusApprovalVo.java** - 附加分审核VO类
2. **XskhAssessmentSelfScoreVo.java** - 自评得分VO类
这两个文件中的 `createTime` 和 `updateTime` 字段被错误地定义为 `String` 类型,但使用了 `@JsonFormat` 注解,导致时间格式不正确。
## 🔧 修复方案
### 修复文件列表
#### 1. XskhAssessmentBonusApprovalVo.java
**文件路径**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentBonusApprovalVo.java`
**修复内容**:
```java
// 修复前
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String updateTime;
// 修复后
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
```
#### 2. XskhAssessmentSelfScoreVo.java
**文件路径**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentSelfScoreVo.java`
**修复内容**:
```java
// 修复前
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String updateTime;
// 修复后
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
```
### 修复原理
1. **类型一致性**: 将时间字段类型从 `String` 改为 `Date`,与实体类保持一致
2. **JSON格式化**: 使用 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")` 注解确保时间以标准格式输出
3. **遵循项目规范**: 符合项目后端开发规范中关于VO类基础字段的要求
## 📊 修复效果
### 修复前
```json
{
"bonusApprovalList": [
{
"createTime": "2025/8/21 上午10:52",
"updateTime": "2025/8/21 上午10:52"
}
]
}
```
### 修复后
```json
{
"bonusApprovalList": [
{
"createTime": "2025-08-21 10:52:40",
"updateTime": "2025-08-21 10:52:40"
}
]
}
```
## 🧪 验证方法
### 1. 代码验证
- ✅ 检查VO类文件中的时间字段类型
- ✅ 确认 `@JsonFormat` 注解正确使用
- ✅ 验证字段类型与实体类一致
### 2. 功能验证
创建了验证脚本 `test/学生考核时间格式修复验证.py`,用于:
- 登录系统获取token
- 调用考核审核接口
- 检查返回数据中的时间格式
- 验证时间字段是否为标准格式
### 3. 测试步骤
```bash
# 1. 启动后端服务
cd backend
mvn spring-boot:run
# 2. 运行验证脚本
cd test
python 学生考核时间格式修复验证.py
```
## 📚 相关规范
### 项目后端开发规范
根据 `.cursor/cursorrules-backend.md` 中的规范:
#### VO类基础字段规范(强制性)
```java
/**
* 创建者
*/
private Long createBy;
/**
* 创建时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/**
* 更新者
*/
private Long updateBy;
/**
* 更新时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
```
#### 字段使用注解规范
- 时间字段必须使用JSON格式化注解
- 字段类型必须统一:createBy/updateBy 为 Long,createTime/updateTime 为 Date
- 时间字段必须使用 `@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")` 注解
## 🔍 预防措施
### 1. 代码审查
- 新增VO类时必须检查基础字段类型
- 确保时间字段使用正确的类型和注解
### 2. 自动化检查
- 在CI/CD流程中添加代码规范检查
- 使用静态代码分析工具检查字段类型
### 3. 文档更新
- 更新项目开发规范文档
- 在团队中推广VO类开发标准
## 📝 总结
本次修复解决了学生考核管理系统中时间格式显示不正确的问题,主要修复了:
1. **2个VO类文件**中的时间字段类型错误
2. **4个时间字段**的类型从String改为Date
3. **确保JSON格式化注解**正确使用
4. **修复Service实现类**中的类型转换错误
### 🔧 修复详情
#### 第一阶段:VO类修复
- `XskhAssessmentBonusApprovalVo.java` - 修复createTime和updateTime字段类型
- `XskhAssessmentSelfScoreVo.java` - 修复createTime和updateTime字段类型
#### 第二阶段:Service实现类修复(优化方案)
- `XskhAssessmentApplicationApprovalServiceImpl.java` - 优化时间字段处理逻辑
- **根本解决方案**:修改SQL查询使用ResultMap而不是Map类型
- **移除复杂的手动类型转换**:让MyBatis自动处理类型映射
- **简化代码**:从50+行手动转换代码简化为1行直接赋值
### 🚨 编译错误解决
在修复VO类后,编译时出现了类型转换错误:
```
不兼容的类型: java.lang.String无法转换为java.util.Date
```
**解决方案**:
1. **修改SQL查询**:使用ResultMap替代resultType="java.util.Map"
2. **添加ResultMap定义**:`XskhAssessmentSelfScoreWithIndicatorResult`
3. **修改Mapper接口**:返回类型从`List<Object>`改为`List<XskhAssessmentSelfScoreVo>`
4. **简化Service代码**:移除复杂的手动类型转换,直接使用MyBatis自动映射
修复后,系统返回的时间格式将符合标准格式 `yyyy-MM-dd HH:mm:ss`,提升了数据的一致性和用户体验。
---
**修复人员**: AI Assistant
**修复时间**: 2025-01-03
**验证状态**: ✅ 已修复
**影响范围**: 学生考核管理系统时间显示
@@ -0,0 +1,246 @@
# 学生考核申请ID精度丢失修复报告
## 问题描述
### 错误现象
在学生考核申请界面保存时,ID等字段丢失精度,导致存到数据库都为 `1958180338534527000`,后几位都是 `000`。
### 问题分析
1. **根本原因**: JavaScript 大整数精度丢失问题
- JavaScript 的 `Number.MAX_SAFE_INTEGER` 为 `9007199254740991`
- 雪花ID通常超过这个范围,导致精度丢失
- 使用 `parseInt()` 转换大整数字符串时,超出安全范围的数字会被截断
2. **问题位置**: `safeConvertId` 函数
- 原函数使用 `parseInt()` 将字符串ID转换为数字
- 对于大整数(如雪花ID),转换后精度丢失
3. **影响范围**: 所有ID字段
- `id`: 主键ID
- `studentId`: 学生ID
- `collegeId`: 学院ID
- `majorId`: 专业ID
- `classId`: 班级ID
- `scheduleId`: 考核计划ID
## 修复方案
### 1. 修复 `safeConvertId` 函数
**文件**: `web/src/views/xskh/assessmentApplication/AssessmentApplicationForm.vue`
**修复前**:
```typescript
const safeConvertId = (id: string | number | undefined): number | undefined => {
if (id === undefined || id === null) return undefined;
if (typeof id === 'number') return id;
if (typeof id === 'string') {
// 对于大整数,使用parseInt而不是Number,并检查是否超出安全范围
const num = parseInt(id, 10);
if (isNaN(num)) return undefined;
// 检查是否超出JavaScript安全整数范围
if (num > Number.MAX_SAFE_INTEGER) {
console.warn('ID超出JavaScript安全整数范围,可能导致精度丢失:', id);
}
return num;
}
return undefined;
};
```
**修复后**:
```typescript
const safeConvertId = (id: string | number | undefined): string | undefined => {
if (id === undefined || id === null) return undefined;
// 始终将ID转换为字符串,避免JavaScript大整数精度丢失
return String(id);
};
```
### 2. 修复要点
#### 2.1 类型安全
- **返回类型**: 从 `number | undefined` 改为 `string | undefined`
- **处理逻辑**: 始终使用 `String()` 转换,保持原始精度
- **兼容性**: 后端API支持 `string | number` 类型,无需修改
#### 2.2 精度保持
- **字符串转换**: 使用 `String()` 而不是 `parseInt()`
- **精度完整**: 保持雪花ID的完整精度
- **无截断**: 避免大整数被截断为 `000`
#### 2.3 项目一致性
- **标准做法**: 与项目中其他模块的处理方式保持一致
- **最佳实践**: 参考项目中已有的成功案例
## 技术原理
### JavaScript 大整数精度问题
#### 1. 安全整数范围
```javascript
Number.MAX_SAFE_INTEGER = 9007199254740991 // 2^53 - 1
Number.MIN_SAFE_INTEGER = -9007199254740991 // -(2^53 - 1)
```
#### 2. 雪花ID特点
- **长度**: 通常为19位数字
- **范围**: 远超JavaScript安全整数范围
- **精度**: 需要保持完整精度
#### 3. 转换对比
```javascript
// 错误的转换方式(精度丢失)
const bigId = "1958180338534527123";
const wrongResult = parseInt(bigId); // 1958180338534527000 (精度丢失)
// 正确的转换方式(保持精度)
const correctResult = String(bigId); // "1958180338534527123" (完整精度)
```
### 后端支持
#### 1. API接口支持
```typescript
// 所有ID参数都支持 string | number 类型
export function getAssessmentApplication(id: string | number) {
return request({
url: '/assessment/application/' + id,
method: 'get'
});
}
```
#### 2. 类型定义支持
```typescript
export interface XskhAssessmentApplicationApprovalBo {
id?: string | number;
studentId?: string | number;
collegeId?: string | number;
majorId?: string | number;
classId?: string | number;
scheduleId?: string | number;
// ... 其他字段
}
```
#### 3. 后端处理
- **Spring Boot**: 自动处理字符串到Long的转换
- **MyBatis-Plus**: 支持字符串类型的ID参数
- **数据库**: bigint类型可以正确处理字符串输入
## 验证测试
### 1. 功能测试
#### 1.1 新增申请测试
- **测试步骤**:
1. 打开学生考核申请界面
2. 选择考核计划
3. 填写申请信息
4. 点击保存
- **预期结果**: ID字段保持完整精度,数据库记录正确
#### 1.2 编辑申请测试
- **测试步骤**:
1. 打开已有申请记录
2. 修改申请信息
3. 点击保存
- **预期结果**: ID字段保持完整精度,更新成功
#### 1.3 提交申请测试
- **测试步骤**:
1. 打开申请记录
2. 点击提交
- **预期结果**: ID字段保持完整精度,提交成功
### 2. 数据验证
#### 2.1 前端数据验证
```javascript
// 验证ID精度
const testId = "1958180338534527123";
const convertedId = safeConvertId(testId);
console.log('转换前:', testId);
console.log('转换后:', convertedId);
console.log('精度保持:', testId === convertedId); // 应该为 true
```
#### 2.2 后端数据验证
```sql
-- 查询保存的记录,验证ID精度
SELECT id, student_id, college_id, major_id, class_id, schedule_id
FROM xskh_assessment_application_approval
WHERE id = '1958180338534527123';
```
### 3. 边界测试
#### 3.1 大整数测试
- **测试数据**: 19位雪花ID
- **预期结果**: 完整精度保持
#### 3.2 小整数测试
- **测试数据**: 普通整数ID
- **预期结果**: 正常转换
#### 3.3 空值测试
- **测试数据**: undefined, null
- **预期结果**: 返回 undefined
## 影响范围
### 1. 直接影响
- ✅ **学生考核申请模块**: 修复完成
- ✅ **ID精度保持**: 所有ID字段完整精度
- ✅ **数据一致性**: 前后端数据一致
### 2. 间接影响
- ✅ **API接口**: 无需修改,已支持字符串类型
- ✅ **数据库**: 无需修改,bigint类型支持字符串输入
- ✅ **其他模块**: 无影响
### 3. 兼容性
- ✅ **向后兼容**: 不影响现有功能
- ✅ **向前兼容**: 支持新的ID格式
- ✅ **跨平台**: 所有平台一致
## 最佳实践总结
### 1. 大整数处理原则
```typescript
// ✅ 正确做法:保持字符串类型
const safeConvertId = (id: string | number | undefined): string | undefined => {
if (id === undefined || id === null) return undefined;
return String(id);
};
// ❌ 错误做法:转换为数字类型
const wrongConvertId = (id: string | number | undefined): number | undefined => {
return parseInt(String(id), 10); // 可能导致精度丢失
};
```
### 2. 项目统一规范
- **ID字段**: 始终使用 `string | number` 类型
- **转换函数**: 保持字符串格式,避免数字转换
- **API接口**: 支持字符串类型参数
- **类型定义**: 明确定义支持的类型
### 3. 测试验证
- **功能测试**: 验证保存、编辑、提交功能
- **数据验证**: 检查数据库记录精度
- **边界测试**: 测试各种ID格式
- **兼容性测试**: 确保向后兼容
## 总结
通过本次修复,成功解决了学生考核申请界面ID精度丢失的问题:
1. **问题根除**: 修复了JavaScript大整数精度丢失的根本原因
2. **精度保持**: 所有ID字段现在保持完整精度
3. **数据一致**: 前后端数据完全一致
4. **标准统一**: 与项目其他模块的处理方式保持一致
5. **向后兼容**: 不影响现有功能和数据
修复方案简单有效,符合项目的最佳实践,为后续类似问题提供了标准解决方案。
@@ -0,0 +1,180 @@
# 学生考核申请LocalDateTime类型转换错误修复报告
## 问题描述
**时间**: 2025-01-03
**接口**: `GET /assessment/application/student/{studentId}/schedule/{scheduleId}`
**错误信息**:
```
{
"code": 500,
"msg": "class java.time.LocalDateTime cannot be cast to class java.lang.String (java.time.LocalDateTime and java.lang.String are in module java.base of loader 'bootstrap')",
"data": null
}
```
## 问题分析
### 错误原因
在 `XskhAssessmentApplicationApprovalServiceImpl.queryById()` 方法中,第83-84行存在类型转换错误:
```java
// 错误的代码
selfScoreVo.setCreateTime((String) scoreMap.get("createTime"));
selfScoreVo.setUpdateTime((String) scoreMap.get("updateTime"));
```
### 根本原因
1. **数据库字段类型**: `create_time` 和 `update_time` 字段在数据库中定义为 `datetime` 类型
2. **MyBatis查询返回**: 通过 `selectSelfScoresWithIndicatorInfo` 查询时,MyBatis可能返回 `LocalDateTime` 类型
3. **强制类型转换**: 代码中直接强制转换为 `String` 类型,导致 `ClassCastException`
### 影响范围
- 学生考核申请详情查询接口
- 学生考核申请表单页面无法正常加载历史数据
## 解决方案
### 1. 修复Service实现类中的类型转换
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentApplicationApprovalServiceImpl.java`
**修改内容**:
```java
// 修复前
selfScoreVo.setCreateTime((String) scoreMap.get("createTime"));
selfScoreVo.setUpdateTime((String) scoreMap.get("updateTime"));
// 修复后
Object createTimeObj = scoreMap.get("createTime");
if (createTimeObj != null) {
selfScoreVo.setCreateTime(createTimeObj.toString());
}
Object updateTimeObj = scoreMap.get("updateTime");
if (updateTimeObj != null) {
selfScoreVo.setUpdateTime(updateTimeObj.toString());
}
```
### 2. 添加JSON格式化注解
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentSelfScoreVo.java`
**修改内容**:
```java
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String updateTime;
```
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentBonusApprovalVo.java`
**修改内容**:
```java
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private String updateTime;
```
**文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentApplicationApprovalVo.java`
**修改内容**:
```java
import com.fasterxml.jackson.annotation.JsonFormat;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date submitTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date teacherApprovalTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
```
## 修复效果
### 修复前
- 接口返回500错误
- 前端无法正常显示考核申请详情
- 用户无法查看历史申请记录
### 修复后
- 接口正常返回数据
- 前端可以正常显示考核申请详情
- 时间字段格式化为标准格式 `yyyy-MM-dd HH:mm:ss`
- 支持查看历史申请记录
## 技术要点
### 1. 类型安全处理
- 使用 `Object` 类型接收查询结果
- 通过 `toString()` 方法安全转换
- 添加空值检查避免NPE
### 2. JSON序列化优化
- 添加 `@JsonFormat` 注解统一时间格式
- 确保前后端时间格式一致
- 提升API响应质量
### 3. 数据库字段映射
- 保持与BaseEntity基类的一致性
- 遵循项目的时间字段处理规范
- 避免类型转换异常
## 验证方法
### 1. 接口测试
```bash
# 测试学生考核申请查询接口
GET /assessment/application/student/1/schedule/1958180338534526978
```
### 2. 前端验证
- 打开学生考核申请表单页面
- 验证历史申请记录正常显示
- 检查时间字段格式是否正确
### 3. 日志检查
- 查看后端日志确认无类型转换错误
- 验证SQL查询正常执行
- 确认数据正确返回
## 预防措施
### 1. 代码规范
- 避免直接强制类型转换
- 使用安全的类型转换方法
- 添加适当的空值检查
### 2. 测试覆盖
- 添加单元测试覆盖时间字段处理
- 集成测试验证API响应格式
- 前端测试确保UI正常显示
### 3. 文档维护
- 更新API文档说明时间字段格式
- 记录VO类字段类型定义
- 维护问题解决方案文档
## 总结
本次修复解决了学生考核申请模块中的LocalDateTime类型转换错误,通过安全的类型处理和JSON格式化注解,确保了接口的稳定性和数据格式的一致性。修复后的代码更加健壮,能够正确处理各种时间类型的数据,提升了用户体验。
---
**修复人员**: AI Assistant
**修复时间**: 2025-01-03
**影响模块**: 学生考核管理模块
**测试状态**: 待验证
@@ -0,0 +1,144 @@
# 学生考核申请更新主键冲突修复报告
## 问题描述
### 现象
在考核申请表单中,对已有数据进行修改后点击保存按钮,调用PUT接口更新数据时出现主键冲突错误:
```
Duplicate entry '1958434137320771585' for key 'PRIMARY'
```
### 错误详情
- **接口**: `PUT /assessment/application`
- **错误类型**: `java.sql.SQLIntegrityConstraintViolationException`
- **错误位置**: `XskhAssessmentSelfScoreMapper.insert`
- **根本原因**: 更新操作误用插入逻辑
## 问题分析
### 根本原因
在 `XskhAssessmentApplicationApprovalServiceImpl.updateSelfScores()` 和 `updateBonusApprovals()` 方法中,更新逻辑存在严重缺陷:
**原有错误逻辑**:
1. 删除所有关联的子表记录
2. 重新插入新的记录
**问题所在**:
- 前端传递的数据包含已存在记录的ID
- 后端删除后重新插入时,由于ID已存在导致主键冲突
- 这种"删除重建"的方式会丢失原有的创建时间、创建人等审计信息
### 影响范围
- 学生考核自评得分表 (`xskh_assessment_self_score`)
- 附加分申请表 (`xskh_assessment_bonus_approval`)
## 解决方案
### 修复策略
采用"智能更新"策略,根据记录ID是否存在决定执行更新或插入操作:
```java
// 如果ID存在,则更新;否则新增
if (selfScore.getId() != null) {
// 更新现有记录
selfScoreMapper.updateById(selfScore);
} else {
// 新增记录
selfScoreMapper.insert(selfScore);
}
```
### 具体修改
#### 1. 修复自评得分更新逻辑
**文件**: `XskhAssessmentApplicationApprovalServiceImpl.java`
**方法**: `updateSelfScores()`
```java
private void updateSelfScores(Long applicationId, List<XskhAssessmentSelfScoreBo> selfScoreList) {
if (selfScoreList != null && !selfScoreList.isEmpty()) {
for (XskhAssessmentSelfScoreBo selfScoreBo : selfScoreList) {
XskhAssessmentSelfScore selfScore = MapstructUtils.convert(selfScoreBo, XskhAssessmentSelfScore.class);
selfScore.setApplicationId(applicationId);
// 如果ID存在,则更新;否则新增
if (selfScore.getId() != null) {
// 更新现有记录
selfScoreMapper.updateById(selfScore);
} else {
// 新增记录
selfScoreMapper.insert(selfScore);
}
}
}
}
```
#### 2. 修复附加分申请更新逻辑
**文件**: `XskhAssessmentApplicationApprovalServiceImpl.java`
**方法**: `updateBonusApprovals()`
```java
private void updateBonusApprovals(Long applicationId, List<XskhAssessmentBonusApprovalBo> bonusApprovalList) {
if (bonusApprovalList != null && !bonusApprovalList.isEmpty()) {
for (XskhAssessmentBonusApprovalBo bonusApprovalBo : bonusApprovalList) {
XskhAssessmentBonusApproval bonusApproval = MapstructUtils.convert(bonusApprovalBo,
XskhAssessmentBonusApproval.class);
bonusApproval.setApplicationId(applicationId);
// 如果ID存在,则更新;否则新增
if (bonusApproval.getId() != null) {
// 更新现有记录
bonusApprovalMapper.updateById(bonusApproval);
} else {
// 新增记录,设置默认状态
bonusApproval.setStatus("1"); // 默认待审核状态
bonusApprovalMapper.insert(bonusApproval);
}
}
}
}
```
## 修复效果
### 优势
1. **保留审计信息**: 不再删除重建,保留原有的创建时间、创建人等字段
2. **避免主键冲突**: 根据ID存在性智能选择更新或插入操作
3. **数据完整性**: 确保数据的连续性和完整性
4. **性能优化**: 避免不必要的删除和插入操作
### 测试验证
- ✅ 新增考核申请:正常插入新记录
- ✅ 修改考核申请:正常更新现有记录
- ✅ 混合操作:同时包含新增和修改的记录
- ✅ 数据完整性:审计字段正确保留
## 经验总结
### 开发规范
1. **更新操作原则**: 避免"删除重建"模式,优先使用"智能更新"
2. **ID处理策略**: 根据ID存在性决定操作类型
3. **审计字段保护**: 更新时注意保护创建时间、创建人等审计信息
4. **事务完整性**: 确保关联数据的更新在同一个事务中
### 代码审查要点
- 检查更新逻辑是否采用"删除重建"模式
- 验证是否正确处理已存在记录的ID
- 确认审计字段的保留策略
- 测试混合操作场景(新增+修改)
## 相关文件
- **修复文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentApplicationApprovalServiceImpl.java`
- **涉及表**:
- `xskh_assessment_self_score` (学生考核自评得分表)
- `xskh_assessment_bonus_approval` (附加分申请表)
- **相关接口**: `PUT /assessment/application`
---
**修复时间**: 2025-08-21
**修复人员**: AI Assistant
**状态**: 已完成
@@ -0,0 +1,153 @@
# 学生考核自评得分查询字段错误修复报告
## 问题描述
**错误时间**: 2025-08-21 11:53:24
**错误接口**: `/assessment/approval/1958361581653856257`
**错误类型**: SQL语法错误 - 未知字段
### 错误详情
```
org.springframework.jdbc.BadSqlGrammarException:
Unknown column 'i.indicator_description' in 'field list'
```
### 错误SQL
```sql
SELECT s.id, s.application_id, s.indicator_id, s.indicator_name,
i.indicator_code, i.indicator_description, s.indicator_type,
s.max_score, s.self_score, s.self_remark, s.teacher_score,
s.teacher_remark, s.create_by, s.create_time, s.update_by, s.update_time
FROM xskh_assessment_self_score s
LEFT JOIN xskh_assessment_indicator i ON s.indicator_id = i.id AND i.tenant_id = '000000'
WHERE s.application_id = ? AND s.deleted = 0 AND s.tenant_id = '000000'
ORDER BY s.create_time ASC
```
## 问题分析
### 根本原因
1. **字段名不匹配**: SQL查询中使用了 `i.indicator_description`,但数据库表中实际字段名为 `description`
2. **数据库表结构**: `xskh_assessment_indicator` 表中的字段名是 `description`,不是 `indicator_description`
### 数据库表结构确认
```sql
-- xskh_assessment_indicator 表结构
CREATE TABLE `xskh_assessment_indicator` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`indicator_code` varchar(50) NOT NULL COMMENT '指标编码',
`indicator_name` varchar(100) NOT NULL COMMENT '指标名称',
`indicator_type_id` bigint(20) NOT NULL COMMENT '指标类型ID',
`indicator_type_name` varchar(100) NOT NULL COMMENT '指标类型名称',
`description` varchar(500) DEFAULT NULL COMMENT '指标说明', -- 正确字段名
`max_score` decimal(5,2) NOT NULL COMMENT '最高分值',
-- ... 其他字段
);
```
## 修复方案
### 修复文件
- **文件路径**: `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentSelfScoreMapper.xml`
- **修复方法**: 将SQL查询中的字段名从 `i.indicator_description` 改为 `i.description`
### 字段命名统一优化
为了保持前后端字段命名的一致性,同时进行了以下优化:
1. **VO类字段统一**: 将 `indicatorDescription` 改为 `description`
2. **Mapper映射统一**: 更新ResultMap中的字段映射
3. **相关文件同步**: 修改了所有相关的Mapper文件和VO类
### 具体修复内容
#### 1. 修复ResultMap映射
```xml
<!-- 修复前 -->
<result property="indicatorDescription" column="indicator_description"/>
<!-- 修复后 -->
<result property="description" column="description"/>
```
#### 2. 修复SQL查询语句
```xml
<!-- 修复前 -->
SELECT
s.id, s.application_id, s.indicator_id, s.indicator_name,
i.indicator_code, i.indicator_description, s.indicator_type,
-- ... 其他字段
<!-- 修复后 -->
SELECT
s.id, s.application_id, s.indicator_id, s.indicator_name,
i.indicator_code, i.description, s.indicator_type,
-- ... 其他字段
```
### 修复后的完整SQL
```sql
SELECT
s.id,
s.application_id,
s.indicator_id,
s.indicator_name,
i.indicator_code,
i.description, -- 修复:使用正确的字段名
s.indicator_type,
s.max_score,
s.self_score,
s.self_remark,
s.teacher_score,
s.teacher_remark,
s.create_by,
s.create_time,
s.update_by,
s.update_time
FROM xskh_assessment_self_score s
LEFT JOIN xskh_assessment_indicator i ON s.indicator_id = i.id AND i.tenant_id = '000000'
WHERE s.application_id = #{applicationId}
AND s.deleted = 0
AND s.tenant_id = '000000'
ORDER BY s.create_time ASC
```
## 验证结果
### 修复验证
1. ✅ **字段映射正确**: ResultMap中的 `description` 字段正确映射到数据库的 `description` 字段
2. ✅ **SQL语法正确**: 查询语句使用正确的字段名,符合数据库表结构
3. ✅ **VO类兼容**: `XskhAssessmentSelfScoreVo` 类中已正确定义 `description` 字段
4. ✅ **字段命名统一**: 前后端字段名保持一致,避免混淆
### 测试建议
1. **接口测试**: 重新调用 `/assessment/approval/{id}` 接口,确认不再出现字段错误
2. **数据验证**: 确认返回的指标说明数据正确显示
3. **功能测试**: 测试学生考核审核功能,确认自评得分查询正常
## 经验总结
### 开发规范提醒
1. **字段命名一致性**: SQL查询中的字段名必须与数据库表结构完全一致
2. **数据库设计规范**: 遵循项目统一的字段命名规范,避免混淆
3. **代码审查**: 在开发过程中注意检查SQL语句的字段名正确性
### 预防措施
1. **数据库文档**: 及时更新数据库设计文档,确保字段名准确
2. **代码规范**: 建立SQL编写规范,要求字段名与数据库表结构保持一致
3. **测试覆盖**: 增加SQL语法检查的自动化测试
## 相关文件
- **数据库设计**: `docs/设计文档/database/学生考核管理/学生考核管理数据库模型.sql`
- **Mapper文件**:
- `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentSelfScoreMapper.xml`
- `backend/ruoyi-modules/ruoyi-xskh/src/main/resources/mapper/xskh/XskhAssessmentPlanMapper.xml`
- **VO类**:
- `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentSelfScoreVo.java`
- `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/domain/vo/XskhAssessmentPlanIndicatorVo.java`
---
**修复人员**: Cursor AI
**修复时间**: 2025-08-21
**修复状态**: ✅ 已完成