解决API文档和路由配置合并冲突

This commit is contained in:
李旭明 committed 2025-08-04 11:19:52 +08:00
commit 79a96a7dfd
662 files changed
+127608 -205195

No files matched your search

+89
View File
@@ -6,7 +6,18 @@
请参考 [API文档基础规范](./API文档基础规范.md) 了解通用的API调用规范、认证方式和响应格式。
<<<<<<< HEAD
## API模块列表
=======
- [学生信息管理API](./学生信息管理API.md)
- [学生信息收集批次管理API](./学生信息收集批次管理API.md)
- [班级信息管理API](./班级信息管理API.md)
- [班级干部信息管理API](./班级干部信息管理API.md)
- [宿舍管理API](./宿舍管理API.md)
- [宿舍管理员信息API](./宿舍管理员信息API.md)
- [宿舍管理员授权API](./宿舍管理员授权API.md)
- [学生管理员授权关系API](./学生管理员授权关系API.md)
>>>>>>> e450a8ffe73adab129a8f940bff3316c0361aa7a
### 1. 用户认证登录
@@ -30,7 +41,34 @@
[心理健康测评API](./心理健康测评API.md) - 包含测评问卷管理、测评结果分析等接口。
<<<<<<< HEAD
### 4. 统计报表
=======
## 宿舍管理员信息API
- [宿舍管理员信息API](./宿舍管理员信息API.md):包含宿舍管理员信息的增删改查接口
- 支持不同类型宿舍管理员(全校管理员、自定义管理员、班主任)的管理
- 提供获取可用教职工信息的接口,支持选择教职工作为宿舍管理员
- **新增功能**: 在添加管理员时,通过下拉选择可用教师,支持模糊搜索,并自动填充选中教师的手机号码和性别信息
**核心功能**:
- 宿舍管理员信息分页查询(支持多条件搜索)
- 宿舍管理员信息新增、修改、删除
- 宿舍管理员信息导出
- 获取可用教职工信息列表(排除已经成为指定类型管理员的人员)
- 教师选择下拉搜索组件,支持模糊搜索和自动填充数据
**API基础路径**: `/xgStudentManager/studentManager`
**最新更新**:
- 2025-07-25: 优化管理员添加界面,将姓名输入框改为可搜索的下拉选择框,选择后自动填充教师信息
- 2025-07-24: 完成宿舍管理员信息管理模块的API接口设计
- 2025-07-24: 添加获取可用的教职工信息列表接口,支持根据管理员类型筛选可用教职工
**详细文档**: [宿舍管理员信息API.md](./宿舍管理员信息API.md)
## 企业信息管理API
>>>>>>> e450a8ffe73adab129a8f940bff3316c0361aa7a
[统计报表API](./统计报表API.md) - 包含各类统计数据查询接口。
@@ -51,8 +89,59 @@
## 更新历史
<<<<<<< HEAD
| 日期 | 版本 | 更新内容 | 更新人 |
|------|------|---------|-------|
| 2025-07-01 | 1.0 | 初始版本 | 系统管理员 |
| 2025-07-15 | 1.1 | 添加心理健康测评API | 张工 |
| 2025-07-30 | 1.2 | 添加预约限制功能API | Cursor |
=======
**详细文档**: [企业信息管理API.md](./企业信息管理API.md)
- [获取当前正在教学的班主任信息](/docs/开发文档/API/宿舍管理员信息API.md#获取当前正在教学的班主任信息)
---
## �� 最新更新日志
### 2025-01-27 - 新增班级学生查询接口
- **新增功能**: 新增 `GET /studentMsg/studentMsg/listByClassId` 接口
- **功能描述**: 查询指定班级中所有在籍学生信息
- **过滤条件**:
- `deleted = 0` (未删除)
- `student_status = '1'` (在籍状态)
- `class_id = 入参的classId` (班级ID)
- `name LIKE '%入参的name%'` (姓名模糊查询,可选)
- **排序方式**: 按创建时间倒序排列
- **权限要求**: `studentMsg:studentMsg:query`
### 2025-07-24 - 学生信息管理界面重构
- **界面重构**: 学生信息管理新增弹窗调整为多tab页形式
- **功能增强**: 新增照片上传功能,支持完整的学生信息录入
- **数据扩展**: 支持60+个学生信息字段,包括:
- 基础信息: 学号、身份证号、姓名、性别、出生日期、民族、籍贯、政治面貌、照片等
- 扩展信息: 文化程度、户口类型、宗教信仰、生源地区、紧急联系人、特殊情况标记等
- 家庭信息: 父母信息、监护人信息、家庭经济状况等
- 健康信息: 身高、体重、血型、健康状况、疾病史、过敏史等
- 学籍信息: 录取类别、学籍状态、入学信息、专业班级、宿舍信息等
- 银行卡信息: 银行卡号、开户银行等
- **技术改进**: 使用el-tabs组件管理多个表单页面,提升用户体验
### 2025-07-01 - API文档架构重构
- **重要重构**: 将原本集中的大型API文档按业务模块拆分,提升维护效率
- **新增**: API文档模板,用于新API文档的生成
- **优化**: 目录结构,按功能模块分类管理
## 🔗 API文档导航
### 👥 学工管理模块
| API文档 | 描述 | 状态 | 更新时间 |
|---------|------|------|----------|
| [学生信息收集批次管理API](./学生信息收集批次管理API.md) | 学生信息收集批次班级权限管理 | ✅ 完成 | 2025-01-27 |
| [学生信息批次收集API](./学生信息批次收集API.md) | 学生信息批次收集管理(新增) | ✅ 完成 | 2025-01-27 |
| [班级信息管理API](./班级信息管理API.md) | 班级基础信息增删改查 | ✅ 完成 | 2025-01-27 |
| [班级干部信息管理API](./班级干部信息管理API.md) | 班级干部信息管理(含用户角色关联) | ✅ 完成 | 2025-01-27 |
| [班干部管理API](./班干部管理API.md) | 班级干部职务管理 | ✅ 完成 | 2025-01-27 |
| [班级教师关联管理API](./班级教师关联管理API.md) | 班主任辅导员关联关系 | ✅ 完成 | 2025-01-27 |
>>>>>>> e450a8ffe73adab129a8f940bff3316c0361aa7a
@@ -42,7 +42,7 @@
| studentNo | string | 否 | 学号 |
| name | string | 否 | 姓名(模糊查询) |
| idCard | string | 否 | 身份证号 |
| gender | string | 否 | 性别:M-男,F-女 |
| gender | string | 否 | 性别:0-男,1-女 |
| studentStatus | string | 否 | 学籍状态 |
| enrollmentYear | integer | 否 | 入学年份 |
| majorCode | string | 否 | 专业代码 |
@@ -0,0 +1,838 @@
# 学生信息批次收集API文档
## 📋 文档信息
| 项目 | 信息 |
|------|------|
| **项目名称** | 校园管理系统 - 学生信息批次收集 |
| **API模块** | 学生信息批次收集管理 |
| **文档版本** | V1.0.0 |
| **创建时间** | 2025-01-27 |
| **基础路径** | `/studentMsgBatch/studentMsgBatch` |
## 🎯 API概述
### 模块说明
学生信息批次收集系统提供学生信息收集、查询、历史记录管理等API接口,支持学生提交个人信息、管理员查询和审核学生信息等功能。
### 技术规范
- **认证方式**: Bearer Token (Sa-Token)
- **数据格式**: JSON
- **响应格式**: 统一使用R类型或TableDataInfo分页格式
- **权限控制**: 基于角色的权限管理(RBAC)
### 权限角色
- **学生**: 查看和提交个人信息
- **班主任**: 查看所带班级学生信息
- **学工管理员**: 查看权限范围内的学生信息
- **系统管理员**: 完整的学生信息管理权限
## 📊 数据模型
### 学生信息批次VO (XgStudentMsgBatchVo)
```java
{
"id": 1, // 主键ID(雪花ID)
"deleted": 0, // 删除标志(0存在 1删除)
"studentNo": "2025001", // 学号
"idCard": "110101199901011234", // 身份证号
"name": "张三", // 姓名
"nameEnglish": "Zhang San", // 英文姓名
"formerName": "", // 曾用名
"gender": "0", // 性别(0-男,1-女)
"birthDate": "1999-01-01", // 出生日期
"nationality": "汉族", // 民族
"nativePlace": "北京市", // 籍贯
"politicalStatus": "团员", // 政治面貌
"hukouLocation": "北京市海淀区", // 户籍所在地
"homeAddress": "北京市海淀区中关村南大街1号", // 家庭地址
"currentAddress": "北京市海淀区学院路8号", // 现住址
"postalCode": "100081", // 邮政编码
"photoUrl": "http://example.com/photo.jpg", // 照片URL
"photoPath": "/upload/photo/2025/01/photo.jpg", // 照片存储路径
"phone": "13800138000", // 手机号码
"isBoarder": 1, // 是否住校(0-否,1-是)
"educationLevel": "本科", // 文化程度
"householdRegisterType": "城镇", // 户口类型
"religion": "无", // 宗教信仰
"sourceRegion": "北京市", // 生源地区
"graduateSchool": "北京市第一中学", // 毕业院校
"email": "zhangsan@example.com", // 电子邮箱
"qq": "123456789", // QQ号码
"wechat": "zhangsan_wx", // 微信号
"maritalStatus": "1", // 婚姻状况(1未婚 2已婚 3离异 4丧偶)
"emergencyContactName": "张父亲", // 紧急联系人姓名
"emergencyContactPhone": "13900139000", // 紧急联系人电话
"emergencyContactRelation": "父亲", // 紧急联系人关系
"isPoorStudent": 0, // 是否贫困生(0-否,1-是)
"isMinority": 0, // 是否少数民族(0-否,1-是)
"isDisabled": 0, // 是否残疾(0-否,1-是)
"isOrphan": 0, // 是否孤儿(0-否,1-是)
"isLeftBehindChild": 0, // 是否留守儿童(0-否,1-是)
"isSingleChild": 1, // 是否独生子女(0-否,1-是)
"isLowIncome": 0, // 是否低保(0-否,1-是)
"hasLoan": 0, // 是否有助学贷款(0-否,1-是)
"loanAmount": 0.0, // 助学贷款金额
"specialties": "篮球、编程", // 特长爱好
"awards": "三好学生", // 获奖情况
"violations": "", // 违纪记录
"socialActivities": "志愿服务活动", // 社会活动
"fatherName": "张父亲", // 父亲姓名
"fatherPhone": "13900139001", // 父亲电话
"fatherWorkUnit": "北京某公司", // 父亲工作单位
"motherName": "张母亲", // 母亲姓名
"motherPhone": "13900139002", // 母亲电话
"motherWorkUnit": "北京某事业单位", // 母亲工作单位
"guardianName": "张父亲", // 监护人姓名
"guardianPhone": "13900139001", // 监护人电话
"guardianRelation": "父亲", // 监护人关系
"familyIncome": 100000.0, // 家庭年收入
"perCapitaIncome": 33333.33, // 人均收入
"incomeSource": "工资收入", // 收入来源
"familyEconomicStatus": "一般", // 家庭经济状况
"familyMembers": 3, // 家庭成员数量
"height": 175.5, // 身高(cm)
"weight": 65.0, // 体重(kg)
"bloodType": "A", // 血型
"healthStatus": "健康", // 健康状况
"specialCondition": "", // 特殊体质或疾病
"medicalHistory": "", // 病史
"allergies": "", // 过敏史
"disabilityType": "", // 残疾类型
"disabilityLevel": "", // 残疾等级
"admissionCategory": "统招", // 录取类别
"studentStatus": "1", // 学籍状态(1在籍 2休学 3复学 4退学 5毕业)
"enrollmentYear": 2025, // 入学年份
"enrollmentDate": "2025-09-01", // 入学日期
"bankCardNo": "6222081234567890123", // 银行卡号
"bankName": "中国工商银行", // 开户银行
"bankBranch": "北京中关村支行", // 开户支行
"cardHolderName": "张三", // 持卡人姓名
"infoStatus": "3", // 信息状态(1正常 2待审核 3审核中 4已驳回 5已锁定)
"dataSource": "manual", // 数据来源(manual手工录入 import批量导入 sync系统同步)
"lastBatchId": 1, // 最后修改批次ID
"lastAuditTime": "2025-01-27 10:00:00", // 最后审核时间
"isComplete": 1, // 信息是否完整(0不完整 1完整)
"completionRate": 95.5, // 完整度百分比
"schoolSystem": 4, // 学制(年)
"academicLevel": "本科", // 学历层次
"trainingType": "全日制", // 培养方式
"majorId": "10001", // 专业ID
"majorName": "计算机科学与技术", // 专业名称
"classId": "20250101", // 班级ID
"className": "计算机2025-1班", // 班级名称
"grade": "2025", // 年级
"collegeId": "1001", // 学院ID
"collegeName": "计算机学院", // 学院名称
"counselorId": 1001, // 辅导员ID
"counselorName": "李辅导员", // 辅导员姓名
"classTeacherId": 1002, // 班主任ID
"classTeacherName": "王老师", // 班主任姓名
"dormitoryBuilding": "1号楼", // 宿舍楼栋
"dormitoryRoom": "101", // 宿舍房间号
"extField1": "", // 扩展字段1
"extField2": "", // 扩展字段2
"extField3": "", // 扩展字段3
"extField4": "", // 扩展字段4
"extField5": "", // 扩展字段5
"remark": "备注信息", // 备注信息
"batchId": 1, // 正在进行中的批次ID
"createBy": 1, // 创建者
"createTime": "2025-01-27 10:00:00", // 创建时间
"updateBy": 1, // 更新者
"updateTime": "2025-01-27 10:00:00" // 更新时间
}
```
## 🔧 API接口列表
### 1. 分页查询学生信息批次收集列表
- **接口路径**: GET `/studentMsgBatch/studentMsgBatch/list`
- **接口功能**: 分页查询学生信息批次收集列表
- **权限要求**: `studentMsgBatch:studentMsgBatch:list`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | Integer | 否 | 页码,默认1 |
| pageSize | Integer | 否 | 每页条数,默认10 |
| batchId | Long | 否 | 批次ID |
| studentNo | String | 否 | 学号 |
| name | String | 否 | 姓名(模糊查询) |
| phone | String | 否 | 手机号码 |
| infoStatus | String | 否 | 信息状态 |
| classId | String | 否 | 班级ID |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功",
"rows": [
{
"id": 1,
"studentNo": "2025001",
"name": "张三",
"phone": "13800138000",
"infoStatus": "3",
"className": "计算机2025-1班",
"createTime": "2025-01-27 10:00:00"
}
],
"total": 100
}
```
### 2. 导出学生信息批次收集列表
- **接口路径**: POST `/studentMsgBatch/studentMsgBatch/export`
- **接口功能**: 导出学生信息批次收集列表到Excel
- **权限要求**: `studentMsgBatch:studentMsgBatch:export`
#### 请求参数
同查询列表接口,用于筛选导出数据。
#### 响应数据
返回Excel文件流。
### 3. 获取学生信息批次收集详细信息
- **接口路径**: GET `/studentMsgBatch/studentMsgBatch/{id}`
- **接口功能**: 根据主键ID获取学生信息详细信息
- **权限要求**: `studentMsgBatch:studentMsgBatch:query`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | Long | 是 | 主键ID |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1,
"studentNo": "2025001",
"name": "张三",
// ... 完整的学生信息字段
}
}
```
### 4. 新增学生信息批次收集
- **接口路径**: POST `/studentMsgBatch/studentMsgBatch`
- **接口功能**: 新增学生信息批次收集记录
- **权限要求**: `studentMsgBatch:studentMsgBatch:add`
#### 请求参数
```json
{
"studentNo": "2025001",
"name": "张三",
"gender": "0",
"phone": "13800138000",
"batchId": 1
// ... 其他学生信息字段
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 5. 修改学生信息批次收集
- **接口路径**: PUT `/studentMsgBatch/studentMsgBatch`
- **接口功能**: 修改学生信息批次收集记录
- **权限要求**: `studentMsgBatch:studentMsgBatch:edit`
#### 请求参数
```json
{
"id": 1,
"studentNo": "2025001",
"name": "张三",
"gender": "0",
"phone": "13800138000",
"batchId": 1
// ... 其他学生信息字段
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 6. 删除学生信息批次收集
- **接口路径**: DELETE `/studentMsgBatch/studentMsgBatch/{ids}`
- **接口功能**: 批量删除学生信息批次收集记录
- **权限要求**: `studentMsgBatch:studentMsgBatch:remove`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | Long[] | 是 | 主键ID数组 |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 7. 查询学生信息完善历史记录
- **接口路径**: GET `/studentMsgBatch/studentMsgBatch/history/{batchId}`
- **接口功能**: 查询学生信息完善的历史记录信息及最后一次提交的信息状态
- **权限要求**: `studentMsgBatch:studentMsgBatch:query`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | Long | 是 | 批次ID |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"historyMsg": [
{
"id": 1,
"name": "张三",
"createTime": "2025-01-27 10:00:00",
"createBy": 1,
"createByName": "张三",
"infoStatus": "3",
"remark": "信息已提交"
}
],
"currentMsg": [
{
"id": 1,
"studentNo": "2025001",
"name": "张三",
// ... 完整的当前信息
}
]
}
}
```
### 8. 获取用户该批次的历史已填写数据
- **接口路径**: GET `/studentMsgBatch/studentMsgBatch/userHistoryData/{batchId}`
- **接口功能**: 获取当前用户在指定批次的历史已填写数据
- **权限要求**: `studentMsgBatch:studentMsgBatch:query`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | Long | 是 | 批次ID |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1,
"studentNo": "2025001",
"name": "张三",
// ... 完整的学生信息字段
}
}
```
### 9. 学生提交信息完善数据
- **接口路径**: POST `/studentMsgBatch/studentMsgBatch/submitStudentMsg`
- **接口功能**: 学生提交信息完善数据
- **权限要求**: `studentMsgBatch:studentMsgBatch:add`
#### 请求参数
```json
{
"id": 1,
"studentNo": "2025001",
"name": "张三",
"gender": "0",
"phone": "13800138000",
"batchId": 1
// ... 其他学生信息字段
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 10. 驳回学生信息提交
- **接口路径**: POST `/studentMsgBatch/studentMsgBatch/rejectStudentMsg`
- **接口功能**: 驳回学生信息提交,设置驳回状态并记录驳回原因
- **权限要求**: `studentMsgBatch:studentMsgBatch:edit`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | Long | 是 | 批次ID |
| userId | Long | 是 | 用户ID(学生ID) |
| remark | String | 是 | 驳回原因 |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功"
}
```
#### 业务逻辑说明
1. 更新 `xg_student_msg_batch` 表中符合条件的记录:
- **更新条件**: `batch_id = #{batchId} AND id = #{userId} AND deleted = 0`
- **更新字段**:
- `remark = #{remark}` (驳回原因)
- `info_status = '4'` (驳回状态)
- `update_by = #{currentUserId}` (当前登录人ID)
- `update_time = NOW()` (当前时间)
2. 记录操作日志,便于审计追踪
#### 使用示例
```javascript
// 驳回学生信息提交
fetch('/studentMsgBatch/studentMsgBatch/rejectStudentMsg', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'batchId=123&userId=456&remark=信息填写不完整,请重新提交'
})
.then(response => response.json())
.then(data => {
if (data.code === 200) {
console.log('驳回成功');
}
});
```
#### 状态说明
| 状态值 | 状态名称 | 说明 |
|--------|----------|------|
| 1 | 待填写 | 学生尚未开始填写信息 |
| 2 | 填写中 | 学生正在填写信息 |
| 3 | 已提交 | 学生已提交信息,等待审核 |
| 4 | 审核不通过 | 管理员驳回,学生需要重新填写 |
| 5 | 审核通过 | 管理员审核通过 |
### 11. 根据部门ID查询学生数据(新增)
- **接口路径**: GET `/studentMsgBatch/studentMsgBatch/listByDept`
- **接口功能**: 根据部门ID查询该组织下所有已完善信息的学生数据
- **权限要求**: `studentMsgBatch:studentMsgBatch:query`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | Long | 是 | 批次ID |
| deptId | Long | 是 | 部门ID |
| name | String | 否 | 学生姓名(模糊查询) |
| studentNo | String | 否 | 学号(精确查询) |
| phone | String | 否 | 手机号码(精确查询) |
| infoStatus | String | 否 | 信息状态 |
| pageNum | Integer | 否 | 页码,默认1 |
| pageSize | Integer | 否 | 每页条数,默认10 |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功",
"rows": [
{
"id": 1,
"studentNo": "2025001",
"name": "张三",
"phone": "13800138000",
"infoStatus": "3",
"className": "计算机2025-1班",
"collegeName": "计算机学院",
"createTime": "2025-01-27 10:00:00"
}
],
"total": 50
}
```
#### 业务逻辑说明
1. 根据 `deptId` 在 `sys_dept` 表中查询该部门及其所有子部门的ID列表
2. 在 `xg_student_msg_batch` 表中查询:
- `batch_id = 入参的batchId`
- `deleted = 0`
- `class_id IN 部门ID列表`(班级ID等于部门ID)
- 支持按姓名、学号、手机号、信息状态等条件过滤
- 分页查询
#### 使用示例
```javascript
// 查询部门ID为101下的所有学生数据
fetch('/studentMsgBatch/studentMsgBatch/listByDept?batchId=1&deptId=101&pageNum=1&pageSize=10')
.then(response => response.json())
.then(data => {
console.log('查询结果:', data);
});
```
### 12. 比较学生信息变更详情(新增)
- **接口路径**: GET `/studentMsgBatch/studentMsgBatch/compareStudentMsg`
- **接口功能**: 比较学生提交信息与原始信息的字段变更详情
- **权限要求**: `studentMsgBatch:studentMsgBatch:query`
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | Long | 是 | 批次ID |
| userId | Long | 是 | 用户ID(学生ID) |
#### 响应数据
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"hasOriginalData": true,
"hasBatchData": true,
"changedFields": [
{
"fieldLabel": "姓名",
"fieldName": "name",
"originalValue": "张三",
"newValue": "张三丰"
},
{
"fieldLabel": "手机号码",
"fieldName": "phone",
"originalValue": "13800138000",
"newValue": "13900139000"
},
{
"fieldLabel": "家庭地址",
"fieldName": "homeAddress",
"originalValue": "北京市海淀区中关村南大街1号",
"newValue": "北京市朝阳区建国门外大街1号"
}
],
"totalChanges": 3,
"originalData": {...},
"batchData": {...}
}
}
```
#### 返回字段说明
| 字段名 | 类型 | 说明 |
|--------|------|------|
| hasOriginalData | Boolean | 是否找到原始学生信息 |
| hasBatchData | Boolean | 是否找到批次提交信息 |
| changedFields | Array | 变更字段列表 |
| totalChanges | Integer | 变更字段总数 |
| originalData | Object | 完整的原始数据 |
| batchData | Object | 完整的批次数据 |
#### changedFields字段结构
| 字段名 | 类型 | 说明 |
|--------|------|------|
| fieldLabel | String | 字段中文名称 |
| fieldName | String | 字段英文名称 |
| originalValue | Object | 原始值(可能为null) |
| newValue | Object | 修改后的值(可能为null) |
#### 业务逻辑说明
1. **数据查询**:
- 原始数据:从 `xg_student_msg` 表查询,条件:`id = userId AND deleted = 0`
- 批次数据:从 `xg_student_msg_batch` 表查询,条件:`id = userId AND batch_id = batchId AND deleted = 0`
2. **字段比较**:
- 比较两个对象的所有业务字段(70+个字段)
- 支持空值安全比较,正确处理null值情况
- 只返回有变更的字段,提高性能和可读性
3. **异常处理**:
- 当原始数据不存在时,返回 `hasOriginalData: false`
- 当批次数据不存在时,返回 `hasBatchData: false`
- 查询异常时返回友好的错误信息
#### 涵盖字段范围
- **基本信息**: 姓名、性别、出生日期、民族、籍贯、政治面貌等
- **联系信息**: 手机号码、邮箱、家庭地址、现住址等
- **家庭信息**: 父母信息、监护人信息、家庭经济状况等
- **健康信息**: 身高、体重、血型、健康状况、过敏史等
- **学籍信息**: 录取类别、学籍状态、入学信息等
- **银行信息**: 银行卡号、开户银行、开户支行等
- **扩展字段**: 5个自定义扩展字段
#### 使用场景
1. **审核前查看**: 管理员审核前查看学生修改了哪些信息
2. **变更记录**: 记录学生信息的变更历史
3. **数据核实**: 对比确认学生提交的修改内容
4. **审计追踪**: 提供完整的信息变更审计轨迹
#### 使用示例
```javascript
// 比较学生ID为1001在批次ID为1中的信息变更
fetch('/studentMsgBatch/studentMsgBatch/compareStudentMsg?batchId=1&userId=1001')
.then(response => response.json())
.then(data => {
console.log('变更详情:', data);
if (data.data.totalChanges > 0) {
console.log('共有', data.data.totalChanges, '个字段发生变更');
data.data.changedFields.forEach(change => {
console.log(`${change.fieldLabel}: ${change.originalValue} → ${change.newValue}`);
});
} else {
console.log('没有字段变更');
}
});
```
## 📝 使用示例
### 前端调用示例
```typescript
import { request } from '@/utils/request';
// 查询学生信息列表
export function getStudentMsgBatchList(params: any) {
return request({
url: '/studentMsgBatch/studentMsgBatch/list',
method: 'get',
params
});
}
// 根据部门查询学生信息
export function getStudentMsgBatchByDept(params: any) {
return request({
url: '/studentMsgBatch/studentMsgBatch/listByDept',
method: 'get',
params
});
}
// 获取学生详细信息
export function getStudentMsgBatchDetail(id: number) {
return request({
url: `/studentMsgBatch/studentMsgBatch/${id}`,
method: 'get'
});
}
// 学生提交信息
export function submitStudentMsg(data: any) {
return request({
url: '/studentMsgBatch/studentMsgBatch/submitStudentMsg',
method: 'post',
data
});
}
```
### 后端测试示例
```java
@Test
public void testQueryByDeptId() {
// 测试根据部门ID查询学生数据
PageQuery pageQuery = new PageQuery();
pageQuery.setPageNum(1);
pageQuery.setPageSize(10);
TableDataInfo<XgStudentMsgBatchVo> result =
xgStudentMsgBatchService.queryPageListByDeptId(
1L, // batchId
101L, // deptId
"张", // name
null, // studentNo
null, // phone
"3", // infoStatus
pageQuery // pageQuery
);
Assert.assertNotNull(result);
Assert.assertTrue(result.getTotal() >= 0);
}
```
## ⚠️ 注意事项
### 数据安全
1. **权限验证**: 所有接口都进行严格的权限验证
2. **数据过滤**: 用户只能查看权限范围内的数据
3. **SQL注入防护**: 使用参数绑定防止SQL注入
### 性能优化
1. **查询优化**: 使用索引优化大数据量查询
2. **部门树查询**: 使用FIND_IN_SET优化部门层级查询
3. **分页查询**: 大列表数据支持分页
### 业务规则
1. **数据关联**: 班级ID等于部门ID的设计规则
2. **状态管理**: 信息状态流转(正常→待审核→审核中→已驳回/已锁定)
3. **逻辑删除**: 支持数据的逻辑删除和恢复
### 错误处理
1. **参数验证**: 对所有输入参数进行验证
2. **异常捕获**: 完善的异常处理机制
3. **友好提示**: 提供用户友好的错误信息
## 📈 版本更新记录
### V1.0.0 (2025-01-27)
- 🆕 新增 `listByDept` 根据部门ID查询学生数据接口
- 🔧 完善学生信息批次收集的完整API接口
- 📝 完善API文档和使用示例
- 🔐 完善权限控制策略
- ✨ 支持部门层级查询和分页功能
### V1.1.0 (2025-01-27)
- 🎯 **审核功能优化**: 审核按钮集成学生信息详情弹窗
- 🔧 **前端集成**: 在学生信息审核界面(`auditDialog.vue`)中集成学生信息完善弹窗(`stuMsgDialog.vue`)
- 📊 **参数传递优化**: 优化batchId和用户ID的传递机制
- 🎨 **用户体验提升**: 从简单审核升级为完整信息查看和编辑
### V1.2.0 (2025-01-27)
- 🆕 **新增比较接口**: `compareStudentMsg` 学生信息变更详情比较接口
- 🔍 **字段对比功能**: 支持70+个字段的全面比较,包括基本信息、家庭信息、健康信息等
- 📋 **变更记录**: 提供详细的字段变更记录,支持原值与新值对比
- 🔒 **权限复用**: 复用现有查询权限,无需额外权限配置
- 📝 **完善文档**: 更新API文档,添加接口使用示例和业务说明
## 🎯 前端审核功能集成
### 审核界面优化说明
**文件路径**: `web/src/views/stuMsgAudit/auditDialog.vue`
#### 功能升级
- **原有功能**: 简单的通过/不通过审核弹窗
- **升级后**: 完整的学生信息查看和编辑界面,包含历史记录、基础信息、扩展信息等
#### 技术实现
```vue
<!-- 审核按钮点击触发 -->
<el-button link type="primary" icon="View" @click="handleAuditStudent(scope.row)">
审核
</el-button>
<!-- 学生信息详情弹窗 -->
<component
:is="StuMsgDialogComponent"
:visible="stuMsgDialogVisible"
:title="stuMsgDialogTitle"
:batchInfo="stuMsgBatchInfo"
@update:visible="stuMsgDialogVisible = $event"
@confirm="handleStuMsgConfirm"
v-if="StuMsgDialogComponent"
/>
```
#### 参数传递
```typescript
const handleAuditStudent = (row: StudentAuditInfo) => {
// 构建传递给学生信息弹窗的批次信息
stuMsgBatchInfo.value = {
batchId: props.batchInfo?.id, // 批次ID
userId: row.id, // 学生用户ID
...props.batchInfo // 其他批次信息
};
stuMsgDialogTitle.value = `学生信息审核 - ${row.name}`;
stuMsgDialogVisible.value = true;
};
```
#### 组件加载
- 使用 `defineAsyncComponent` 动态加载组件
- 避免静态导入可能的类型错误
- 支持按需加载,优化性能
```typescript
const StuMsgDialogComponent = defineAsyncComponent(() => import('./stuMsgDialog.vue'));
```
### 集成效果
1. **操作流程**:
- 点击学生列表中的"审核"按钮
- 弹出完整的学生信息详情界面
- 包含历史记录、基础信息、扩展信息、家庭信息等多个tab页
- 支持信息查看和编辑
- 确认后自动刷新学生列表
2. **数据传递**:
- 自动传入当前批次的`batchId`
- 传入当前学生的`userId`
- 包含完整的批次配置信息
3. **用户体验**:
- 无缝的界面切换
- 丰富的学生信息展示
- 完整的操作历史追踪
这次优化显著提升了审核功能的实用性和用户体验,从简单的审核操作升级为完整的学生信息管理功能。
@@ -0,0 +1,608 @@
# 学生信息收集批次管理API文档
## 📋 文档信息
| 项目 | 信息 |
|------|------|
| **项目名称** | 校园管理系统 - 学生信息收集批次管理 |
| **API模块** | 学生信息收集批次班级权限管理 |
| **文档版本** | V1.0.0 |
| **创建时间** | 2025-01-27 |
| **基础路径** | `/stuColBatchClass/stuColBatchClass` |
## 🎯 API概述
### 模块说明
学生信息收集批次管理系统提供完整的批次权限配置、批次查询、权限范围管理等API接口,支持按班级权限控制学生信息收集范围。
### 技术规范
- **认证方式**: Bearer Token (Sa-Token)
- **数据格式**: JSON
- **响应格式**: 统一使用R类型或TableDataInfo分页格式
- **权限控制**: 基于角色的权限管理(RBAC)
### 权限角色
- **学生管理员**: 查看权限范围内的批次信息
- **班主任**: 查看所带班级的批次信息
- **全校学生管理员**: 查看全校范围的批次信息
- **系统管理员**: 完整的批次权限配置管理
## 📊 数据模型
### 批次信息VO (XgStuColBatchVo)
```java
{
"id": 1, // 批次ID
"batchName": "2025年春季学期信息收集", // 批次名称
"description": "收集学生基本信息", // 批次描述
"startTime": "2025-02-01 00:00:00", // 开始时间
"endTime": "2025-02-28 23:59:59", // 结束时间
"status": "0", // 状态(0正常 1停用)
"collectionType": "1", // 收集类型(1-新学期收集 2-其他)
"remark": "备注信息", // 备注
"createBy": 1, // 创建者
"createTime": "2025-01-27 10:00:00", // 创建时间
"updateBy": 1, // 更新者
"updateTime": "2025-01-27 10:00:00" // 更新时间
}
```
### 批次班级权限VO (XgStuColBatchClassVo)
```java
{
"id": 1, // 主键ID
"batchId": 1, // 批次ID
"classId": 101, // 班级ID
"className": "计算机2023-1", // 班级名称
"majorId": 10, // 专业ID
"majorName": "计算机科学与技术", // 专业名称
"collegeId": 1, // 院系ID
"collegeName": "计算机学院", // 院系名称
"remark": "备注", // 备注
"deleted": 0, // 删除标志
"createBy": 1, // 创建者
"createTime": "2025-01-27 10:00:00", // 创建时间
"updateBy": 1, // 更新者
"updateTime": "2025-01-27 10:00:00" // 更新时间
}
```
## 🔧 API接口列表
### 1. 基础管理接口
#### 1.1 查询批次班级权限列表
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/list`
- **权限标识**: `stuColBatchClass:stuColBatchClass:list`
- **功能描述**: 分页查询学生信息收集批次班级权限列表
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | integer | 否 | 页码,默认1 |
| pageSize | integer | 否 | 每页大小,默认10 |
| batchId | long | 否 | 批次ID |
| classId | long | 否 | 班级ID |
| className | string | 否 | 班级名称(模糊查询) |
| majorName | string | 否 | 专业名称(模糊查询) |
| collegeName | string | 否 | 院系名称(模糊查询) |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 1,
"batchId": 1,
"classId": 101,
"className": "计算机2023-1",
"majorId": 10,
"majorName": "计算机科学与技术",
"collegeId": 1,
"collegeName": "计算机学院"
}
],
"total": 1
}
```
#### 1.2 获取批次班级权限详细信息
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/{id}`
- **权限标识**: `stuColBatchClass:stuColBatchClass:query`
- **功能描述**: 根据主键获取批次班级权限详细信息
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | long | 是 | 主键ID |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 1,
"batchId": 1,
"classId": 101,
"className": "计算机2023-1",
"majorId": 10,
"majorName": "计算机科学与技术",
"collegeId": 1,
"collegeName": "计算机学院",
"remark": "备注",
"deleted": 0
}
}
```
#### 1.3 新增批次班级权限
- **接口路径**: POST `/stuColBatchClass/stuColBatchClass`
- **权限标识**: `stuColBatchClass:stuColBatchClass:add`
- **功能描述**: 新增学生信息收集批次班级权限
**请求参数**:
```json
{
"batchId": 1,
"classId": 101,
"className": "计算机2023-1",
"majorId": 10,
"majorName": "计算机科学与技术",
"collegeId": 1,
"collegeName": "计算机学院",
"remark": "备注"
}
```
**响应数据**:
```json
{
"code": 200,
"msg": "新增成功"
}
```
#### 1.4 修改批次班级权限
- **接口路径**: PUT `/stuColBatchClass/stuColBatchClass`
- **权限标识**: `stuColBatchClass:stuColBatchClass:edit`
- **功能描述**: 修改学生信息收集批次班级权限
**请求参数**:
```json
{
"id": 1,
"batchId": 1,
"classId": 101,
"className": "计算机2023-1",
"majorId": 10,
"majorName": "计算机科学与技术",
"collegeId": 1,
"collegeName": "计算机学院",
"remark": "修改后的备注"
}
```
**响应数据**:
```json
{
"code": 200,
"msg": "修改成功"
}
```
#### 1.5 删除批次班级权限
- **接口路径**: DELETE `/stuColBatchClass/stuColBatchClass/{ids}`
- **权限标识**: `stuColBatchClass:stuColBatchClass:remove`
- **功能描述**: 删除学生信息收集批次班级权限(支持批量删除)
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | string | 是 | 主键ID,多个用逗号分隔 |
**响应数据**:
```json
{
"code": 200,
"msg": "删除成功"
}
```
#### 1.6 导出批次班级权限
- **接口路径**: POST `/stuColBatchClass/stuColBatchClass/export`
- **权限标识**: `stuColBatchClass:stuColBatchClass:export`
- **功能描述**: 导出学生信息收集批次班级权限列表
**请求参数**:
```json
{
"batchId": 1,
"classId": 101,
"className": "计算机",
"majorName": "计算机科学",
"collegeName": "计算机学院"
}
```
**响应数据**:
- 直接返回Excel文件流
### 2. 权限范围管理接口
#### 2.1 获取批次收集范围
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/getScope/{batchId}`
- **权限标识**: `stuColBatchClass:stuColBatchClass:query`
- **功能描述**: 获取指定批次配置的班级权限范围
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | long | 是 | 批次ID |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": [101, 102, 103] // 班级ID列表
}
```
#### 2.2 获取批次权限范围详情
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/getScopeDetail/{batchId}`
- **权限标识**: `stuColBatchClass:stuColBatchClass:query`
- **功能描述**: 获取批次配置的权限范围详细信息
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | long | 是 | 批次ID |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"batchId": 1,
"classId": 101,
"className": "计算机2023-1",
"majorId": 10,
"majorName": "计算机科学与技术",
"collegeId": 1,
"collegeName": "计算机学院"
}
]
}
```
#### 2.3 设置批次收集范围
- **接口路径**: POST `/stuColBatchClass/stuColBatchClass/setScope`
- **权限标识**: `stuColBatchClass:stuColBatchClass:setScope`
- **功能描述**: 设置学生信息收集批次班级权限范围
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | long | 是 | 批次ID |
| deptIds | array | 是 | 授权的班级ID列表 |
**响应数据**:
```json
{
"code": 200,
"msg": "权限设置成功,共设置 3 个班级权限"
}
```
### 3. 用户相关接口
#### 3.1 查询学生可参与的批次
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/getStudentAvailableBatches`
- **权限标识**: 无需权限(基于当前登录用户)
- **功能描述**: 查询当前登录学生可参与的批次信息
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchName | string | 否 | 批次名称(模糊查询) |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"batchId": 1,
"batchName": "2025年春季学期信息收集",
"description": "收集学生基本信息",
"startTime": "2025-02-01 00:00:00",
"endTime": "2025-02-28 23:59:59",
"status": "0",
"collectionType": "1",
"remark": "备注信息",
"classId": 101,
"className": "计算机2023-1",
"majorId": 10,
"majorName": "计算机科学与技术",
"collegeId": 1,
"collegeName": "计算机学院"
}
]
}
```
#### 3.2 查询老师关联的批次信息 🆕
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/getTeacherRelatedBatches`
- **权限标识**: `stuColBatchClass:stuColBatchClass:query`
- **功能描述**: 查询当前登录老师有权限管理的批次信息
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchName | string | 否 | 批次名称(模糊查询) |
**业务逻辑**:
1. 调用 `XgStudentManagerSourceServiceImpl.getCurrentUserAuthDeptTree()` 获取当前用户有权限的部门树
2. 递归提取所有部门ID
3. 根据部门ID查询关联的批次信息
4. 支持全校学生管理员、普通管理员、班主任三种权限模式
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"batchName": "2025年春季学期信息收集",
"description": "收集学生基本信息",
"startTime": "2025-02-01 00:00:00",
"endTime": "2025-02-28 23:59:59",
"status": "0",
"collectionType": "1",
"remark": "备注信息",
"createBy": 1,
"createTime": "2025-01-27 10:00:00",
"updateBy": 1,
"updateTime": "2025-01-27 10:00:00"
}
]
}
```
#### 3.3 获取批次组织权限范围 🆕
- **接口路径**: GET `/stuColBatchClass/stuColBatchClass/getBatchAuthScope/{batchId}`
- **权限标识**: `stuColBatchClass:stuColBatchClass:query`
- **功能描述**: 获取用户对应该批次的组织权限范围,返回过滤后的组织树形结构
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| batchId | long | 是 | 批次ID |
**业务逻辑**:
1. **获取用户权限**: 调用 `XgStudentManagerSourceServiceImpl.getCurrentUserAuthDeptTree()` 获取当前用户所有的组织权限信息
2. **查询批次班级**: 通过batchId在 `xg_stu_col_batch_class` 中获取class_id数据(过滤条件:`deleted=0`)
3. **过滤组织树**: 将第一步获取的树形数据中,非第二步中存在的班级节点都剔除,只保留在第二步中存在的班级ID以及其上级节点,直到树形数据的顶点
**核心算法**:
- 使用递归算法实现树形结构过滤
- 保留目标班级节点及其所有上级节点
- 确保组织层级关系的完整性
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"label": "计算机学院",
"parentId": 101,
"deptCategory": "college",
"weight": 1,
"disabled": false,
"children": [
{
"id": 10,
"label": "计算机科学与技术",
"parentId": 1,
"deptCategory": "major",
"weight": 1,
"disabled": false,
"children": [
{
"id": 101,
"label": "计算机2023-1班",
"parentId": 10,
"deptCategory": "class",
"weight": 1,
"disabled": false,
"children": []
}
]
}
]
}
]
}
```
**异常情况**:
- 当批次ID无效时返回空列表
- 当用户无权限时返回空列表
- 当批次未配置班级权限时返回空列表
**应用场景**:
- 前端组织树选择器的数据源
- 批次权限范围可视化显示
- 权限验证和范围限制
## 🔐 权限设计
### 权限控制策略
#### 1. 全校学生管理员
- **权限范围**: 可以查看和管理所有批次信息
- **识别条件**: `xg_student_manager` 表中 `manager_type = '1'`
- **部门权限**: 获取 `dept_id = 101` 下所有部门数据
#### 2. 普通管理员
- **权限范围**: 基于 `xg_student_manager_source` 表中的授权范围
- **识别条件**: `xg_student_manager` 表中 `manager_type != '1'`
- **部门权限**: 根据授权的部门ID获取相关部门树
#### 3. 班主任
- **权限范围**: 只能查看所带班级的批次信息
- **识别条件**: `xg_class_msg` 表中 `class_teacher_id` 等于当前用户ID
- **自动权限**: 系统自动将班主任权限合并到部门权限中
### 数据查询策略
#### 权限范围计算
```sql
-- 1. 查询用户授权的部门ID
SELECT auth_source FROM xg_student_manager_source
WHERE manager_id IN (
SELECT id FROM xg_student_manager WHERE user_id = #{currentUserId}
) AND status = '0' AND deleted = '0'
-- 2. 查询班主任权限
SELECT id FROM xg_class_msg
WHERE class_teacher_id = #{currentUserId} AND deleted = '0'
-- 3. 查询批次信息
SELECT DISTINCT b.* FROM xg_stu_col_batch b
INNER JOIN xg_stu_col_batch_class bc ON b.id = bc.batch_id
WHERE bc.class_id IN (权限范围内的部门ID)
AND b.deleted = 0 AND b.status = '0'
```
## 📝 使用示例
### 前端调用示例
#### JavaScript/Vue示例
```javascript
// 查询老师关联的批次信息
async function getTeacherBatches(batchName = '') {
try {
const response = await request({
url: '/stuColBatchClass/stuColBatchClass/getTeacherRelatedBatches',
method: 'get',
params: {
batchName: batchName
}
});
if (response.code === 200) {
console.log('批次列表:', response.data);
return response.data;
} else {
console.error('查询失败:', response.msg);
return [];
}
} catch (error) {
console.error('请求异常:', error);
return [];
}
}
// 设置批次权限范围
async function setBatchScope(batchId, deptIds) {
try {
const response = await request({
url: '/stuColBatchClass/stuColBatchClass/setScope',
method: 'post',
data: {
batchId: batchId,
deptIds: deptIds
}
});
if (response.code === 200) {
console.log('权限设置成功:', response.msg);
return true;
} else {
console.error('设置失败:', response.msg);
return false;
}
} catch (error) {
console.error('请求异常:', error);
return false;
}
}
```
### 移动端调用示例
#### uni-app示例
```javascript
// 查询学生可参与的批次
async function getStudentBatches() {
try {
const response = await uni.request({
url: baseURL + '/stuColBatchClass/stuColBatchClass/getStudentAvailableBatches',
method: 'GET',
header: {
'Authorization': 'Bearer ' + getToken(),
'Content-Type': 'application/json'
}
});
if (response.data.code === 200) {
return response.data.data;
} else {
uni.showToast({
title: response.data.msg || '查询失败',
icon: 'none'
});
return [];
}
} catch (error) {
console.error('请求异常:', error);
uni.showToast({
title: '网络异常',
icon: 'none'
});
return [];
}
}
```
## ⚠️ 注意事项
### 数据安全
1. **权限验证**: 所有接口都进行严格的权限验证
2. **数据过滤**: 用户只能查看权限范围内的数据
3. **SQL注入防护**: 使用参数绑定防止SQL注入
### 性能优化
1. **查询优化**: 使用索引优化大数据量查询
2. **缓存策略**: 部门权限树适合进行缓存
3. **分页查询**: 大列表数据支持分页
### 错误处理
1. **参数验证**: 对所有输入参数进行验证
2. **异常捕获**: 完善的异常处理机制
3. **友好提示**: 提供用户友好的错误信息
## 📈 版本更新记录
### V1.0.0 (2025-01-27)
- 🆕 新增 `getTeacherRelatedBatches` 接口
- 🔧 修复 `XgStuColBatchVo` 缺少基础字段的问题
- 📝 完善API文档和使用示例
- 🔐 完善权限控制策略
- ✨ 支持全校管理员、普通管理员、班主任三种权限模式
@@ -0,0 +1,625 @@
# 学生信息管理API文档
## 1. 文档说明
### 1.1 API基础信息
- **模块名称**: 学工管理 - 学生信息管理
- **模块代码**: ruoyi-xg
- **基础路径**: `/studentMsg/studentMsg`
- **权限前缀**: `studentMsg:studentMsg`
- **版本**: 1.0.0
- **更新时间**: 2025-01-27(新增班级学生查询接口)
### 1.2 通用规范
- **请求方式**: 支持GET、POST、PUT、DELETE
- **数据格式**: JSON
- **字符编码**: UTF-8
- **认证方式**: Bearer Token
- **权限控制**: Sa-Token + 数据权限
### 1.3 响应格式
```json
{
"code": 200,
"msg": "操作成功",
"data": {},
"timestamp": "2025-01-27T18:00:00"
}
```
## 2. 学生信息管理接口
### 2.1 查询学生信息列表
- **接口路径**: GET `/studentMsg/studentMsg/list`
- **权限标识**: `studentMsg:studentMsg:list`
- **功能描述**: 分页查询学生信息列表,支持多条件筛选
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | integer | 否 | 页码,默认1 |
| pageSize | integer | 否 | 每页大小,默认10 |
| studentNo | string | 否 | 学号 |
| name | string | 否 | 姓名(模糊查询) |
| idCard | string | 否 | 身份证号 |
| gender | string | 否 | 性别:0-男,1-女 |
| studentStatus | string | 否 | 学籍状态 |
| phone | string | 否 | 联系电话(模糊查询) |
| email | string | 否 | 邮箱 |
| classId | string | 否 | 班级ID |
| className | string | 否 | 班级名称 |
| majorId | string | 否 | 专业ID |
| majorName | string | 否 | 专业名称 |
| collegeId | string | 否 | 学院ID |
| collegeName | string | 否 | 学院名称 |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 1,
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1",
"createTime": "2025-01-27 10:00:00",
"updateTime": "2025-01-27 10:00:00"
}
],
"total": 1
}
```
### 2.2 根据组织ID查询学生信息列表
- **接口路径**: POST `/studentMsg/studentMsg/listByDeptId`
- **权限标识**: `studentMsg:studentMsg:list`
- **功能描述**: 根据部门ID查询该组织下所有学生信息
#### 请求参数
```json
{
"dept_id": 101,
"name": "张",
"studentNo": "2023001",
"phone": "138",
"studentStatus": "1",
"beginTime": "2025-01-01",
"endTime": "2025-01-27"
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"rows": [...],
"total": 10
}
```
### 2.3 查询班级中所有学生信息 ⭐ 新增
- **接口路径**: GET `/studentMsg/studentMsg/listByClassId`
- **权限标识**: `studentMsg:studentMsg:query`
- **功能描述**: 查询指定班级中所有在籍学生信息
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| classId | string | 是 | 班级ID |
| name | string | 否 | 学生姓名(模糊查询) |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1",
"createTime": "2025-01-27 10:00:00",
"updateTime": "2025-01-27 10:00:00"
}
]
}
```
#### 业务逻辑
1. 在 `xg_student_msg` 表中查询:
- `deleted = 0` (未删除)
- `student_status = '1'` (在籍状态)
- `class_id = 入参的classId` (班级ID)
- `name LIKE '%入参的name%'` (姓名模糊查询,可选)
- 按创建时间倒序排列
### 2.4 获取学生信息详情
- **接口路径**: GET `/studentMsg/studentMsg/{id}`
- **权限标识**: `studentMsg:studentMsg:query`
- **功能描述**: 根据ID获取学生详细信息
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | long | 是 | 学生ID |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 1,
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1",
"createTime": "2025-01-27 10:00:00",
"updateTime": "2025-01-27 10:00:00"
}
}
```
### 2.5 新增学生信息
- **接口路径**: POST `/studentMsg/studentMsg`
- **权限标识**: `studentMsg:studentMsg:add`
- **功能描述**: 新增学生信息
#### 请求参数
```json
{
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1"
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "新增成功"
}
```
### 2.6 修改学生信息
- **接口路径**: PUT `/studentMsg/studentMsg`
- **权限标识**: `studentMsg:studentMsg:edit`
- **功能描述**: 修改学生信息
#### 请求参数
```json
{
"id": 1,
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1"
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "修改成功"
}
```
### 2.7 删除学生信息
- **接口路径**: DELETE `/studentMsg/studentMsg/{ids}`
- **权限标识**: `studentMsg:studentMsg:remove`
- **功能描述**: 批量删除学生信息
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | string | 是 | 学生ID列表,多个用逗号分隔 |
#### 响应数据
```json
{
"code": 200,
"msg": "删除成功"
}
```
### 2.8 导出学生信息
- **接口路径**: POST `/studentMsg/studentMsg/export`
- **权限标识**: `studentMsg:studentMsg:export`
- **功能描述**: 导出学生信息列表
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| studentNo | string | 否 | 学号 |
| name | string | 否 | 姓名 |
| studentStatus | string | 否 | 学籍状态 |
#### 响应数据
返回Excel文件流
### 2.8.1 高级检索导出学生信息
- **接口路径**: POST `/studentMsg/studentMsg/advancedSearchExport`
- **权限标识**: `studentMsg:studentMsg:export`
- **功能描述**: 根据高级检索条件导出学生信息列表,支持多维度复合查询条件
- **版本**: 1.2.1 (2025-01-07修复MapStruct转换错误)
- **修复**: 解决 `XgStudentMsgVo` 到 `XgStudentMsgExportVo` 转换器缺失问题
#### 请求参数
支持以下高级检索条件:
**基础信息检索**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| studentNo | string | 否 | 学号(模糊查询) |
| name | string | 否 | 姓名(模糊查询) |
| idCard | string | 否 | 身份证号(模糊查询) |
| phone | string | 否 | 手机号码(模糊查询) |
| email | string | 否 | 邮箱(模糊查询) |
| gender | array | 否 | 性别数组:0-男,1-女 |
| nationality | array | 否 | 民族数组 |
| politicalStatus | array | 否 | 政治面貌数组 |
| birthDateStart | string | 否 | 出生日期开始:yyyy-MM-dd |
| birthDateEnd | string | 否 | 出生日期结束:yyyy-MM-dd |
**学籍信息检索**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| studentStatus | array | 否 | 学籍状态数组:1-在籍,2-休学,3-复学,4-退学,5-毕业 |
| enrollmentYearStart | integer | 否 | 入学年份开始 |
| enrollmentYearEnd | integer | 否 | 入学年份结束 |
| admissionCategory | array | 否 | 录取类别数组 |
**学院专业班级检索**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| collegeName | string | 否 | 学院名称(模糊查询) |
| majorName | string | 否 | 专业名称(模糊查询) |
| className | string | 否 | 班级名称(模糊查询) |
| grade | array | 否 | 年级数组 |
| counselorName | string | 否 | 辅导员姓名(模糊查询) |
| classTeacherName | string | 否 | 班主任姓名(模糊查询) |
**家庭信息检索**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| fatherName | string | 否 | 父亲姓名(模糊查询) |
| motherName | string | 否 | 母亲姓名(模糊查询) |
| familyEconomicStatus | array | 否 | 家庭经济状况数组 |
| familyIncomeMin | number | 否 | 家庭收入最小值 |
| familyIncomeMax | number | 否 | 家庭收入最大值 |
**特殊身份检索**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| isPoorStudent | array | 否 | 是否贫困生:0-否,1-是 |
| isMinority | array | 否 | 是否少数民族:0-否,1-是 |
| isDisabled | array | 否 | 是否残疾:0-否,1-是 |
| isOrphan | array | 否 | 是否孤儿:0-否,1-是 |
**权限范围**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| deptIds | array | 否 | 部门ID数组,用于权限控制 |
#### 响应数据
返回Excel文件流,包含完整的学生信息数据
#### 修复记录
- **2025-01-07 v1.2.1**: 修复MapStruct转换错误,解决 `XgStudentMsgVo` 到 `XgStudentMsgExportVo` 转换器缺失问题
- **问题**: `MapstructUtils.convert()` 无法找到两个VO类之间的转换器,导致导出功能报错
- **解决**: 在Controller中添加手动转换方法 `convertToExportVo()` 和 `convertToExportVoList()`
- **影响**: 导出功能正常工作,所有字段完整映射
- **2025-01-07 v1.2.0**: 修复分页限制问题,导出时pageSize设置为50000,确保能导出所有符合条件的记录
- **问题**: 之前由于BO类默认pageSize=10,导致只能导出10条记录
- **解决**: 取消注释Controller中的分页参数设置代码
### 2.9 查询学生个人信息
- **接口路径**: GET `/studentMsg/studentMsg/personalInfo`
- **权限标识**: `studentMsg:studentMsg:query`
- **功能描述**: 查询当前登录学生的个人信息
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| userId | long | 否 | 用户ID,为空时使用当前登录用户ID |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 1,
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1",
"createTime": "2025-01-27 10:00:00",
"updateTime": "2025-01-27 10:00:00"
}
}
```
### 2.10 根据班级ID查询学生信息列表
- **接口路径**: GET `/studentMsg/studentMsg/listByClassId`
- **权限标识**: `studentMsg:studentMsg:query`
- **功能描述**: 查询指定班级中所有在籍学生信息
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| classId | string | 是 | 班级ID |
| name | string | 否 | 学生姓名(模糊查询) |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"studentNo": "2023001001",
"name": "张三",
"gender": "0",
"birthDate": "2000-01-01",
"idCard": "110101199901010001",
"phone": "13800138000",
"email": "zhangsan@example.com",
"classId": "101",
"className": "计算机2023-1班",
"majorId": "CS001",
"majorName": "计算机科学与技术",
"collegeId": "CS",
"collegeName": "计算机学院",
"studentStatus": "1",
"createTime": "2025-01-27 10:00:00",
"updateTime": "2025-01-27 10:00:00"
}
]
}
```
### 2.11 导入学生信息数据
- **接口路径**: POST `/studentMsg/studentMsg/importData`
- **权限标识**: `studentMsg:studentMsg:import`
- **功能描述**: 批量导入学生信息数据,支持Excel文件上传
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file | MultipartFile | 是 | Excel文件(.xlsx格式) |
| updateSupport | boolean | 否 | 是否支持更新已存在数据,默认false |
#### 请求示例
```http
POST /studentMsg/studentMsg/importData
Content-Type: multipart/form-data
file: [Excel文件]
updateSupport: true
```
#### 响应数据
```json
{
"code": 200,
"msg": "恭喜您,数据已全部导入成功!共 10 条,数据如下:",
"data": null
}
```
#### 错误响应示例
```json
{
"code": 500,
"msg": "很抱歉,导入失败!共 2 条数据格式不正确,错误如下:<br/>1、学号 2023001001 已存在<br/>2、学号 2023001002 导入失败:手机号码格式不正确",
"data": null
}
```
### 2.12 获取导入模板
- **接口路径**: POST `/studentMsg/studentMsg/importTemplate`
- **权限标识**: `studentMsg:studentMsg:import`
- **功能描述**: 下载学生信息导入模板文件
#### 请求参数
无
#### 响应数据
返回Excel模板文件(.xlsx格式)
#### 模板字段说明
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| 学号 | string | 是 | 学生学号,必须唯一 |
| 姓名 | string | 是 | 学生姓名 |
| 性别 | string | 是 | 0-男,1-女 |
| 身份证号 | string | 否 | 身份证号码 |
| 手机号码 | string | 否 | 手机号码,必须唯一 |
| 电子邮箱 | string | 否 | 邮箱地址,必须唯一 |
| 出生日期 | date | 否 | 出生日期 |
| 民族 | string | 否 | 民族代码 |
| 政治面貌 | string | 否 | 政治面貌代码 |
| 家庭地址 | string | 否 | 家庭住址 |
| 现住址 | string | 否 | 现居住地址 |
| 班级ID | string | 否 | 班级编号 |
| 班级名称 | string | 否 | 班级名称 |
| 专业ID | string | 否 | 专业编号 |
| 专业名称 | string | 否 | 专业名称 |
| 学院ID | string | 否 | 学院编号 |
| 学院名称 | string | 否 | 学院名称 |
| 学籍状态 | string | 否 | 1-在籍,2-休学,3-复学,4-退学,5-毕业 |
## 3. 数据字典
### 3.1 性别字典
| 字典值 | 字典标签 |
|--------|----------|
| 0 | 男 |
| 1 | 女 |
### 3.2 学籍状态字典
| 字典值 | 字典标签 |
|--------|----------|
| 1 | 在籍 |
| 2 | 休学 |
| 3 | 复学 |
| 4 | 退学 |
| 5 | 毕业 |
### 3.3 信息状态字典
| 字典值 | 字典标签 |
|--------|----------|
| 1 | 正常 |
| 2 | 待审核 |
| 3 | 审核中 |
| 4 | 已驳回 |
| 5 | 已锁定 |
## 4. 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 操作成功 |
| 400 | 请求参数错误 |
| 401 | 未授权 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
## 5. 权限说明
### 5.1 数据权限控制
- **超级管理员**: 可查看所有学生信息
- **学工管理员**: 可查看所有学生信息
- **二级学院管理员**: 只能查看本学院学生信息
- **辅导员**: 只能查看负责班级的学生信息
- **班主任**: 只能查看负责班级的学生信息
- **学生**: 只能查看自己的信息
### 5.2 操作权限控制
- **新增**: 学工管理员、二级学院管理员
- **修改**: 学工管理员、二级学院管理员、辅导员、班主任(限制范围)
- **删除**: 学工管理员、二级学院管理员
- **导入**: 学工管理员、二级学院管理员
- **导出**: 学工管理员、二级学院管理员、辅导员、班主任
- **查询**: 所有有权限的用户
## 6. 注意事项
1. 所有接口都需要Bearer Token认证
2. 删除操作为逻辑删除,不会物理删除数据
3. 学号和身份证号必须唯一
4. 新增学生时会自动创建对应的系统用户账号
5. 支持多租户数据隔离
6. 所有时间字段使用yyyy-MM-dd HH:mm:ss格式
7. 数据权限按照用户角色自动过滤
8. 班级学生查询接口只返回在籍状态的学生
## 7. 更新日志
### 2025-01-27 - 新增学生信息导入功能
- **新增功能**: 新增学生信息批量导入功能
- **新增接口**:
- `POST /studentMsg/studentMsg/importData` - 学生信息导入接口
- `POST /studentMsg/studentMsg/importTemplate` - 导入模板下载接口
- **功能特性**:
- 支持Excel文件批量导入
- 支持新增和更新两种模式
- 自动验证学号、手机号、邮箱唯一性
- 完整的错误处理和结果统计
- 自动创建对应的系统用户账号
- **技术实现**:
- 使用自定义Listener处理Excel解析
- 事务保证数据一致性
- 详细的导入结果反馈
### 2025-01-27 - 新增班级学生查询接口
- **新增功能**: 新增 `GET /studentMsg/studentMsg/listByClassId` 接口
- **功能描述**: 查询指定班级中所有在籍学生信息
- **过滤条件**:
- `deleted = 0` (未删除)
- `student_status = '1'` (在籍状态)
- `class_id = 入参的classId` (班级ID)
- `name LIKE '%入参的name%'` (姓名模糊查询,可选)
- **排序方式**: 按创建时间倒序排列
---
**文档更新时间**: 2025-01-07
**文档版本**: 1.2.0
**维护人员**: Cursor AI
**更新说明**:
- 新增高级检索导出接口文档
- 修复高级检索导出分页限制问题
- 之前版本:新增学生信息导入功能,完善学生信息管理API文档
@@ -0,0 +1,357 @@
# 学生管理员授权关系API文档
## 1. 文档说明
### 1.1 API基础信息
- **模块名称**: 学工管理 - 学生管理员授权关系
- **模块代码**: ruoyi-xg
- **基础路径**: `/xgStudentManagerSource/studentManagerSource`
- **权限前缀**: `xgStudentManagerSource:studentManagerSource`
- **版本**: 1.0.0
- **更新时间**: 2025-07-24
### 1.2 通用规范
- **请求方式**: 支持GET、POST、PUT、DELETE
- **数据格式**: JSON
- **字符编码**: UTF-8
- **认证方式**: Bearer Token
- **权限控制**: Sa-Token + 数据权限
### 1.3 响应格式
```json
{
"code": 200,
"msg": "操作成功",
"data": {},
"timestamp": "2025-07-24T10:00:00"
}
```
## 2. 学生管理员授权关系接口
### 2.1 查询学生管理员授权关系列表
- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/list`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:list`
- **功能描述**: 分页查询学生管理员授权关系列表
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | integer | 否 | 页码,默认1 |
| pageSize | integer | 否 | 每页大小,默认10 |
| managerId | long | 否 | 管理员ID |
| permissionType | string | 否 | 权限类型(1查看 2编辑 3完全管理) |
| authType | string | 否 | 授权类型(1全校级 2自定义 3班主任) |
| authSource | long | 否 | 授权来源ID(对应班级id) |
| status | string | 否 | 状态(0有效 1已撤销) |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"total": 10,
"rows": [
{
"id": 1,
"managerId": 123,
"permissionType": "2",
"authType": "2",
"authSource": 456,
"status": "0",
"remark": "自定义管理员权限",
"createTime": "2025-07-24 10:00:00"
}
]
}
```
### 2.2 获取学生管理员授权关系详细信息
- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/{id}`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:query`
- **功能描述**: 根据主键查询学生管理员授权关系详情
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | long | 是 | 主键ID |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 1,
"managerId": 123,
"permissionType": "2",
"authType": "2",
"authSource": 456,
"status": "0",
"remark": "自定义管理员权限",
"createTime": "2025-07-24 10:00:00",
"updateTime": "2025-07-24 10:00:00"
}
}
```
### 2.3 新增学生管理员授权关系
- **接口路径**: POST `/xgStudentManagerSource/studentManagerSource`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:add`
- **功能描述**: 新增学生管理员授权关系
#### 请求参数
```json
{
"managerId": 123,
"permissionType": "2",
"authType": "2",
"authSource": 456,
"status": "0",
"remark": "自定义管理员权限"
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "新增成功"
}
```
### 2.4 修改学生管理员授权关系
- **接口路径**: PUT `/xgStudentManagerSource/studentManagerSource`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:edit`
- **功能描述**: 修改学生管理员授权关系
#### 请求参数
```json
{
"id": 1,
"managerId": 123,
"permissionType": "3",
"authType": "2",
"authSource": 456,
"status": "0",
"remark": "修改后的权限说明"
}
```
#### 响应数据
```json
{
"code": 200,
"msg": "修改成功"
}
```
### 2.5 删除学生管理员授权关系
- **接口路径**: DELETE `/xgStudentManagerSource/studentManagerSource/{ids}`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:remove`
- **功能描述**: 删除学生管理员授权关系(支持批量删除)
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | long[] | 是 | 主键ID数组 |
#### 响应数据
```json
{
"code": 200,
"msg": "删除成功"
}
```
### 2.6 自定义学生管理员授予部门权限
- **接口路径**: POST `/xgStudentManagerSource/studentManagerSource/grantDeptAuth`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:grantDeptAuth`
- **功能描述**: 为指定管理员批量授予多个部门的权限
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| managerId | long | 是 | 管理员ID |
| authSourceIds | long[] | 是 | 资源ID列表(部门ID数组) |
#### 响应数据
```json
{
"code": 200,
"msg": "授权成功"
}
```
### 2.7 获取指定管理员的授权资源名称列表
- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/authSourceNames`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:query`
- **功能描述**: 获取指定管理员已授权的资源名称列表
#### 请求参数
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| managerId | long | 是 | 管理员ID |
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": [
"计算机学院",
"计算机2023-1班",
"计算机2023-2班"
]
}
```
### 2.8 获取当前用户的学生管理权限部门树 🆕
- **接口路径**: GET `/xgStudentManagerSource/studentManagerSource/currentUserAuthTree`
- **权限标识**: `xgStudentManagerSource:studentManagerSource:query`
- **功能描述**: 获取当前登录用户的学生管理权限部门树形结构,只有班级节点可以点击
#### 请求参数
无需入参,自动获取当前登录用户信息
#### 接口逻辑
1. **第一步**: 通过当前登录用户ID,关联 `xg_student_manager` 与 `xg_student_manager_source` 表,获取授权来源信息
- 查询条件:`xg_student_manager.user_id = 当前用户ID AND xg_student_manager.deleted = '0' AND xg_student_manager.status = '0'`
- 关联条件:`xg_student_manager_source.manager_id = xg_student_manager.id AND xg_student_manager_source.status = '0' AND xg_student_manager_source.deleted = '0'`
2. **第二步**: 根据授权来源关联 `sys_dept` 表获取部门数据和所有子节点
- 查询条件:`sys_dept.dept_id IN (auth_source列表) AND sys_dept.status = '0' AND sys_dept.del_flag = '0'`
- 同时获取所有子部门:通过 `ancestors` 字段查找子节点
3. **第三步**: 向上查找父节点直到根节点(id=101)
- 通过 `ancestors` 字段解析父节点层级关系
- 构建完整的部门树结构
#### 响应数据
```json
{
"code": 200,
"msg": "查询成功",
"data": [
{
"deptId": 101,
"parentId": null,
"deptName": "杭州职业技术学院",
"deptCategory": "university",
"orderNum": 1,
"clickable": false,
"children": [
{
"deptId": 102,
"parentId": 101,
"deptName": "计算机学院",
"deptCategory": "college",
"orderNum": 1,
"clickable": false,
"children": [
{
"deptId": 103,
"parentId": 102,
"deptName": "计算机科学与技术专业",
"deptCategory": "major",
"orderNum": 1,
"clickable": false,
"children": [
{
"deptId": 104,
"parentId": 103,
"deptName": "计算机2023-1班",
"deptCategory": "class",
"orderNum": 1,
"clickable": true,
"children": []
},
{
"deptId": 105,
"parentId": 103,
"deptName": "计算机2023-2班",
"deptCategory": "class",
"orderNum": 2,
"clickable": true,
"children": []
}
]
}
]
}
]
}
]
}
```
#### 响应字段说明
| 字段名 | 类型 | 说明 |
|--------|------|------|
| deptId | long | 部门ID |
| parentId | long | 父部门ID |
| deptName | string | 部门名称 |
| deptCategory | string | 部门类别编码 |
| orderNum | integer | 显示顺序 |
| clickable | boolean | 是否可点击(只有班级节点可点击) |
| children | array | 子部门列表 |
#### 使用说明
- 该接口无需传参,自动获取当前登录用户的权限范围
- 返回的树形结构包含完整的层级关系,从根节点到叶子节点
- `clickable` 字段标识节点是否可点击,只有班级节点(包含"班"字或dept_category包含"class")设置为true
- 前端可根据 `clickable` 字段控制节点的交互行为
- 树形结构按 `orderNum` 字段排序
## 3. 数据字典
### 3.1 权限类型 (permissionType)
| 值 | 说明 |
|----|------|
| 1 | 查看 |
| 2 | 编辑 |
| 3 | 完全管理 |
### 3.2 授权类型 (authType)
| 值 | 说明 |
|----|------|
| 1 | 全校级 |
| 2 | 自定义 |
| 3 | 班主任 |
### 3.3 状态 (status)
| 值 | 说明 |
|----|------|
| 0 | 有效 |
| 1 | 已撤销 |
## 4. 权限说明
### 4.1 数据权限控制
- **超级管理员**: 可管理所有学生管理员授权关系
- **学工管理员**: 可管理所有学生管理员授权关系
- **二级学院管理员**: 只能管理本学院相关的授权关系
- **普通用户**: 只能查看自己的权限信息
### 4.2 操作权限控制
- **查询**: 所有有相关权限的用户
- **新增**: 学工管理员、二级学院管理员
- **修改**: 学工管理员、二级学院管理员
- **删除**: 学工管理员、二级学院管理员
- **授权**: 学工管理员、二级学院管理员
## 5. 注意事项
1. 所有接口都需要Bearer Token认证
2. 删除操作为逻辑删除,不会物理删除数据
3. 授权关系具有层级性,上级权限包含下级权限
4. 支持多租户数据隔离
5. 获取当前用户权限树接口会自动过滤无权限的部门
6. 部门树构建时会自动补全父节点层级关系
---
**文档更新时间**: 2025-07-24
**文档版本**: 1.0.0
**维护人员**: Cursor AI
**更新说明**: 新增学生管理员授权关系管理接口,包含获取当前用户权限部门树功能
@@ -0,0 +1,259 @@
# 宿舍管理员信息API文档
## 1. API概述
本文档提供宿舍管理员信息相关的API接口说明,包括管理员信息的查询、新增、修改、删除等功能,以及获取可用教师列表的接口。
## 2. 基本信息
- **模块名称**:宿舍管理员信息管理
- **接口前缀**:`/xgStudentManager/studentManager`
- **数据权限**:需要相应的权限才能访问接口
## 3. API列表
### 3.1 查询宿舍管理员信息列表
- **接口地址**:`/xgStudentManager/studentManager/list`
- **请求方式**:GET
- **权限标识**:`xgStudentManager:studentManager:list`
- **请求参数**:
| 参数名 | 类型 | 是否必须 | 描述 |
|------------|--------|----------|-----------------------------------------------|
| managerName| string | 否 | 管理员姓名,支持模糊查询 |
| managerType| string | 否 | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
| managerLevel| number | 否 | 管理级别(1-3,1为最高) |
| phone | string | 否 | 联系电话 |
| gender | string | 否 | 性别(0男 1女 2未知) |
| status | string | 否 | 状态(0正常 1停用) |
| pageNum | number | 是 | 页码 |
| pageSize | number | 是 | 每页记录数 |
- **返回示例**:
```json
{
"total": 2,
"rows": [
{
"id": 1,
"managerName": "张三",
"managerType": "1",
"managerLevel": 1,
"userId": 103,
"phone": "13800138001",
"gender": "0",
"status": "0",
"remark": "全校管理员",
"createTime": "2025-07-24 10:00:00",
"updateTime": "2025-07-24 10:00:00"
}
],
"code": 200,
"msg": "查询成功"
}
```
### 3.2 获取宿舍管理员信息详细信息
- **接口地址**:`/xgStudentManager/studentManager/{id}`
- **请求方式**:GET
- **权限标识**:`xgStudentManager:studentManager:query`
- **请求参数**:
| 参数名 | 类型 | 是否必须 | 描述 |
|-------|--------|----------|-------|
| id | number | 是 | 主键ID |
- **返回示例**:
```json
{
"data": {
"id": 1,
"managerName": "张三",
"managerType": "1",
"managerLevel": 1,
"userId": 103,
"phone": "13800138001",
"gender": "0",
"status": "0",
"remark": "全校管理员",
"createTime": "2025-07-24 10:00:00",
"updateTime": "2025-07-24 10:00:00"
},
"code": 200,
"msg": "操作成功"
}
```
### 3.3 新增宿舍管理员信息
- **接口地址**:`/xgStudentManager/studentManager`
- **请求方式**:POST
- **权限标识**:`xgStudentManager:studentManager:add`
- **请求参数**:
| 参数名 | 类型 | 是否必须 | 描述 |
|------------|--------|----------|-----------------------------------------------|
| managerName| string | 是 | 管理员姓名 |
| managerType| string | 是 | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
| managerLevel| number | 是 | 管理级别(1-3,1为最高) |
| userId | number | 是 | 关联用户ID |
| phone | string | 是 | 联系电话 |
| gender | string | 是 | 性别(0男 1女 2未知) |
| status | string | 否 | 状态(0正常 1停用),默认0 |
| remark | string | 否 | 备注 |
- **返回示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 3.4 修改宿舍管理员信息
- **接口地址**:`/xgStudentManager/studentManager`
- **请求方式**:PUT
- **权限标识**:`xgStudentManager:studentManager:edit`
- **请求参数**:
| 参数名 | 类型 | 是否必须 | 描述 |
|------------|--------|----------|-----------------------------------------------|
| id | number | 是 | 主键ID |
| managerName| string | 是 | 管理员姓名 |
| managerType| string | 是 | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
| managerLevel| number | 是 | 管理级别(1-3,1为最高) |
| userId | number | 是 | 关联用户ID |
| phone | string | 是 | 联系电话 |
| gender | string | 是 | 性别(0男 1女 2未知) |
| status | string | 否 | 状态(0正常 1停用) |
| remark | string | 否 | 备注 |
- **返回示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 3.5 删除宿舍管理员信息
- **接口地址**:`/xgStudentManager/studentManager/{ids}`
- **请求方式**:DELETE
- **权限标识**:`xgStudentManager:studentManager:remove`
- **请求参数**:
| 参数名 | 类型 | 是否必须 | 描述 |
|-------|--------|----------|-----------------------|
| ids | array | 是 | 主键ID数组,格式:[1,2] |
- **返回示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 3.6 导出宿舍管理员信息
- **接口地址**:`/xgStudentManager/studentManager/export`
- **请求方式**:POST
- **权限标识**:`xgStudentManager:studentManager:export`
- **请求参数**:与查询列表接口参数相同
- **返回内容**:Excel文件
### 3.7 获取可用教师列表
- **接口地址**:`/xgStudentManager/studentManager/listAvailableTeachers`
- **请求方式**:GET
- **权限标识**:`xgStudentManager:studentManager:list`
- **请求参数**:
| 参数名 | 类型 | 是否必须 | 描述 |
|------------|--------|----------|-----------------------------------------------|
| managerType| string | 是 | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
- **返回示例**:
```json
{
"data": [
{
"id": 101,
"managerName": "张三",
"phone": "13800138001",
"gender": "0"
},
{
"id": 102,
"managerName": "李四",
"phone": "13800138002",
"gender": "1"
}
],
"code": 200,
"msg": "操作成功"
}
```
## 获取当前正在教学的班主任信息
- **接口路径**:`/xgStudentManager/studentManager/listActiveClassTeachers`
- **请求方式**:GET
- **权限标识**:`xgStudentManager:studentManager:list`
- **入参**:
- `phone`(可选,string):手机号,模糊查询
- `name`(可选,string):姓名,模糊查询
- **出参**:
- `name`:姓名
- `phone`:手机号
- `sex`:性别
- `className`:班级名称
- **返回示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"name": "张三",
"phone": "13800001111",
"sex": "0",
"className": "2025级1班"
}
]
}
```
- **说明**:
- 仅返回当前“正在教学”的班主任(班级状态为1,未删除,班主任ID有效,用户状态正常)。
- 支持姓名和手机号的模糊查询,均为可选参数。
## 4. 数据结构说明
### 4.1 宿舍管理员对象
| 字段名 | 类型 | 描述 |
|------------|--------|-----------------------------------------------|
| id | number | 主键ID |
| managerName| string | 管理员姓名 |
| managerType| string | 管理员类型(1 全校管理员 2 自定义管理员 3 班主任) |
| managerLevel| number | 管理级别(1-3,1为最高) |
| userId | number | 关联用户ID |
| phone | string | 联系电话 |
| gender | string | 性别(0男 1女 2未知) |
| status | string | 状态(0正常 1停用) |
| deleted | string | 删除标志(0代表存在 1代表删除) |
| remark | string | 备注 |
| createBy | number | 创建者 |
| createTime | date | 创建时间 |
| updateBy | number | 更新者 |
| updateTime | date | 更新时间 |
## 5. 错误码说明
| 错误码 | 描述 | 解决方案 |
|-------|----------------|-----------------------------|
| 500 | 系统内部错误 | 联系系统管理员 |
| 401 | 未授权 | 检查用户权限或重新登录 |
| 400 | 请求参数错误 | 检查请求参数是否符合要求 |
@@ -51,7 +51,7 @@
| pageSize | Integer | 是 | 每页记录数 |
| name | String | 否 | 姓名,支持模糊查询 |
| idCard | String | 否 | 身份证号 |
| gender | String | 否 | 性别(M-男 F-女) |
| gender | String | 否 | 性别(0-男 1-女) |
| examNumber | String | 否 | 准考证号 |
| phone1 | String | 否 | 联系电话 |
| examRegion | String | 否 | 中考地区 |
@@ -67,7 +67,7 @@
| id | Long | 主键ID |
| name | String | 姓名 |
| idCard | String | 身份证号 |
| gender | String | 性别(M-男 F-女) |
| gender | String | 性别(0-男 1-女) |
| phone1 | String | 联系电话 |
| examNumber | String | 准考证号 |
| examRegion | String | 中考地区 |
@@ -99,7 +99,7 @@
| id | Long | 主键ID |
| name | String | 姓名 |
| idCard | String | 身份证号 |
| gender | String | 性别(M-男 F-女) |
| gender | String | 性别(0-男 1-女) |
| isBoarding | Integer | 是否住校(0-否 1-是) |
| birthDate | Date | 出生年月 |
| phone1 | String | 联系电话1 |
@@ -141,7 +141,7 @@
|---------|--------|---------|-----|
| name | String | 是 | 姓名 |
| idCard | String | 是 | 身份证号 |
| gender | String | 是 | 性别(M-男 F-女) |
| gender | String | 是 | 性别(0-男 1-女) |
| isBoarding | Integer | 否 | 是否住校(0-否 1-是) |
| birthDate | Date | 否 | 出生年月 |
| phone1 | String | 是 | 联系电话1 |
@@ -190,7 +190,7 @@
| id | Long | 是 | 主键ID |
| name | String | 是 | 姓名 |
| idCard | String | 是 | 身份证号 |
| gender | String | 是 | 性别(M-男 F-女) |
| gender | String | 是 | 性别(0-男 1-女) |
| isBoarding | Integer | 否 | 是否住校(0-否 1-是) |
| birthDate | Date | 否 | 出生年月 |
| phone1 | String | 是 | 联系电话1 |
@@ -37,7 +37,7 @@ public class EnrollmentInfoVo {
private Long id; // 主键ID
private String name; // 姓名
private String idCard; // 身份证号
private String gender; // 性别(M-男 F-女)
private String gender; // 性别(0-男 1-女)
private Boolean isBoarding; // 是否住校
private Date birthDate; // 出生日期
private String phone1; // 联系手机号码1
@@ -0,0 +1,415 @@
# 班级干部信息管理API文档
## 📋 文档信息
| 项目 | 信息 |
|------|------|
| **模块名称** | 班级干部信息管理 |
| **模块代码** | ruoyi-xg |
| **基础路径** | `/xgClassPosMsg/xgClassPosMsg` |
| **权限前缀** | `xgClassPosMsg:xgClassPosMsg` |
| **版本** | V1.0.0 |
| **创建时间** | 2025-01-27 |
| **更新时间** | 2025-01-27 |
## 🎯 API概述
### 模块说明
班级干部信息管理模块提供完整的班级干部信息管理功能,包括班级干部的任命、任期管理、状态跟踪等全生命周期管理,以及用户角色关联功能。
### 技术规范
- **认证方式**: Bearer Token (Sa-Token)
- **数据格式**: JSON
- **响应格式**: 统一使用R类型或TableDataInfo分页格式
- **权限控制**: 基于角色的权限管理(RBAC)
### 核心特性
- **基础管理**: 班级干部信息的增删改查、导入导出
- **任期管理**: 任职日期、任期时长、到期提醒
- **状态跟踪**: 在任、到期、辞职、免职、调任、休学等状态
- **用户角色关联**: 自动建立学生与角色的关联关系
- **数据权限**: 支持按部门查询班级干部信息
## 📊 数据模型
### 班级干部信息VO (XgClassPosMsgVo)
```java
{
"id": 1, // 主键ID(雪花ID)
"deleted": 0, // 删除标志(0代表存在 1代表删除)
"classId": 101, // 班级ID,关联xg_class_msg.id
"className": "计算机2023-1班", // 班级名称(联表查询获取)
"studentId": 1001, // 学生ID,关联xg_student_msg.id
"studentNo": "2023001", // 学号
"studentName": "张三", // 学生姓名(冗余字段)
"positionCode": "monitor", // 职务编码,关联xg_class_pos_dict.position_code
"positionName": "班长", // 职务名称(联表查询获取)
"appointmentDate": "2024-09-01", // 任职日期
"termStartDate": "2024-09-01", // 任期开始日期
"termEndDate": "2025-07-01", // 任期结束日期
"termDuration": 10, // 任期时长(月)
"isCurrent": 1, // 是否现任:0-否,1-是
"cadreStatus": "1", // 学生职务任职状态:1-在任,2-到期,3-辞职,4-免职,5-调任,6-休学
"appointmentReason": "综合表现优秀", // 任命原因
"appointmentMethod": "appointment", // 学生职务任命方式:appointment-任命,election-选举,recommendation-推荐
"dismissalDate": null, // 离任日期
"dismissalReason": null, // 离任原因
"remark": "备注信息", // 备注信息
"createBy": 1, // 创建者
"createTime": "2024-09-01 10:00:00", // 创建时间
"updateBy": 1, // 更新者
"updateTime": "2024-09-01 10:00:00" // 更新时间
}
```
## 🔧 API接口列表
### 1. 基础管理接口
#### 1.1 查询班级干部信息列表
- **接口路径**: GET `/xgClassPosMsg/xgClassPosMsg/list`
- **权限标识**: `xgClassPosMsg:xgClassPosMsg:list`
- **功能描述**: 分页查询班级干部信息列表,支持按部门查询
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | integer | 否 | 页码,默认1 |
| pageSize | integer | 否 | 每页大小,默认10 |
| deptId | long | 否 | 部门ID,用于按部门查询班级干部信息 |
| classId | long | 否 | 班级ID |
| studentId | long | 否 | 学生ID |
| studentNo | string | 否 | 学号(模糊查询) |
| studentName | string | 否 | 学生姓名(模糊查询) |
| positionCode | string | 否 | 职务编码 |
| appointmentDate | string | 否 | 任职日期 |
| termStartDate | string | 否 | 任期开始日期 |
| termEndDate | string | 否 | 任期结束日期 |
| termDuration | integer | 否 | 任期时长(月) |
| isCurrent | integer | 否 | 是否现任:0-否,1-是 |
| cadreStatus | string | 否 | 学生职务任职状态 |
| appointmentReason | string | 否 | 任命原因 |
| appointmentMethod | string | 否 | 学生职务任命方式 |
| dismissalDate | string | 否 | 离任日期 |
| dismissalReason | string | 否 | 离任原因 |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 1,
"classId": 101,
"className": "计算机2023-1班",
"studentId": 1001,
"studentNo": "2023001",
"studentName": "张三",
"positionCode": "monitor",
"positionName": "班长",
"appointmentDate": "2024-09-01",
"termStartDate": "2024-09-01",
"termEndDate": "2025-07-01",
"termDuration": 10,
"isCurrent": 1,
"cadreStatus": "1",
"appointmentReason": "综合表现优秀",
"appointmentMethod": "appointment",
"createTime": "2024-09-01 10:00:00"
}
],
"total": 1
}
```
#### 1.2 获取班级干部信息详情
- **接口路径**: GET `/xgClassPosMsg/xgClassPosMsg/{id}`
- **权限标识**: `xgClassPosMsg:xgClassPosMsg:query`
- **功能描述**: 根据主键获取班级干部信息详细信息
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| id | long | 是 | 主键ID |
**响应数据**:
```json
{
"code": 200,
"msg": "查询成功",
"data": {
"id": 1,
"classId": 101,
"className": "计算机2023-1班",
"studentId": 1001,
"studentNo": "2023001",
"studentName": "张三",
"positionCode": "monitor",
"positionName": "班长",
"appointmentDate": "2024-09-01",
"termStartDate": "2024-09-01",
"termEndDate": "2025-07-01",
"termDuration": 10,
"isCurrent": 1,
"cadreStatus": "1",
"appointmentReason": "综合表现优秀",
"appointmentMethod": "appointment",
"dismissalDate": null,
"dismissalReason": null,
"remark": "备注信息",
"createTime": "2024-09-01 10:00:00",
"updateTime": "2024-09-01 10:00:00"
}
}
```
#### 1.3 新增班级干部信息
- **接口路径**: POST `/xgClassPosMsg/xgClassPosMsg`
- **权限标识**: `xgClassPosMsg:xgClassPosMsg:add`
- **功能描述**: 新增班级干部信息,同时自动建立用户角色关联
**请求参数**:
```json
{
"classId": 101,
"studentId": 1001,
"studentNo": "2023001",
"studentName": "张三",
"positionCode": "monitor",
"appointmentDate": "2024-09-01",
"termStartDate": "2024-09-01",
"termEndDate": "2025-07-01",
"termDuration": 10,
"isCurrent": 1,
"cadreStatus": "1",
"appointmentReason": "综合表现优秀",
"appointmentMethod": "appointment",
"remark": "备注信息"
}
```
**响应数据**:
```json
{
"code": 200,
"msg": "新增成功"
}
```
**重要说明**:
- 新增班级干部信息时,系统会自动向 `sys_user_role` 表插入一条数据
- `user_id` = 当前记录的 `studentId`
- `role_id` = 当前记录的 `id`(班级干部信息ID)
- 此操作在事务中进行,确保数据一致性
#### 1.4 修改班级干部信息
- **接口路径**: PUT `/xgClassPosMsg/xgClassPosMsg`
- **权限标识**: `xgClassPosMsg:xgClassPosMsg:edit`
- **功能描述**: 修改班级干部信息
**请求参数**:
```json
{
"id": 1,
"classId": 101,
"studentId": 1001,
"studentNo": "2023001",
"studentName": "张三",
"positionCode": "monitor",
"appointmentDate": "2024-09-01",
"termStartDate": "2024-09-01",
"termEndDate": "2025-07-01",
"termDuration": 10,
"isCurrent": 1,
"cadreStatus": "1",
"appointmentReason": "综合表现优秀",
"appointmentMethod": "appointment",
"remark": "修改后的备注信息"
}
```
**响应数据**:
```json
{
"code": 200,
"msg": "修改成功"
}
```
#### 1.5 删除班级干部信息
- **接口路径**: DELETE `/xgClassPosMsg/xgClassPosMsg/{ids}`
- **权限标识**: `xgClassPosMsg:xgClassPosMsg:remove`
- **功能描述**: 删除班级干部信息(支持批量删除),同时删除对应的用户角色关联
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | string | 是 | 主键ID,多个用逗号分隔 |
**响应数据**:
```json
{
"code": 200,
"msg": "删除成功"
}
```
**重要说明**:
- 删除班级干部信息时,系统会自动删除 `sys_user_role` 表中对应的角色关联
- 删除操作会先清理关联数据,再删除主数据
- 此操作在事务中进行,确保数据一致性
#### 1.6 导出班级干部信息
- **接口路径**: POST `/xgClassPosMsg/xgClassPosMsg/export`
- **权限标识**: `xgClassPosMsg:xgClassPosMsg:export`
- **功能描述**: 导出班级干部信息列表
**请求参数**: 同列表查询参数
**响应数据**: Excel文件下载
## 🔐 用户角色关联功能
### 功能说明
班级干部信息管理模块集成了用户角色关联功能,确保班级干部信息与系统权限管理的一致性。
### 关联规则
1. **新增关联**: 新增班级干部信息时,自动建立用户角色关联
- `sys_user_role.user_id` = `xg_class_pos_msg.student_id`
- `sys_user_role.role_id` = `xg_class_pos_msg.id`
2. **删除关联**: 删除班级干部信息时,自动删除对应的用户角色关联
- 根据 `role_id` 删除 `sys_user_role` 表中的记录
### 事务处理
- 所有用户角色关联操作都在事务中进行
- 关联操作失败不会影响主业务流程,但会记录警告日志
- 使用 `@Transactional(rollbackFor = Exception.class)` 确保数据一致性
### 异常处理
- 用户角色关联操作异常会被捕获并记录日志
- 不会抛出异常影响主业务流程
- 通过日志记录操作结果和异常信息
## 📝 数据字典
### 学生职务任职状态 (cadre_status)
| 编码 | 名称 | 说明 |
|------|------|------|
| 1 | 在任 | 正常任职状态 |
| 2 | 到期 | 任期已结束 |
| 3 | 辞职 | 主动辞职 |
| 4 | 免职 | 被免职 |
| 5 | 调任 | 调任其他职务 |
| 6 | 休学 | 因休学暂停职务 |
### 学生职务任命方式 (appointment_method)
| 编码 | 名称 | 说明 |
|------|------|------|
| appointment | 任命 | 直接任命 |
| election | 选举 | 通过选举产生 |
| recommendation | 推荐 | 通过推荐产生 |
### 是否现任 (is_current)
| 编码 | 名称 | 说明 |
|------|------|------|
| 0 | 否 | 非现任职务 |
| 1 | 是 | 现任职务 |
## 🔒 权限说明
### 权限标识列表
| 权限标识 | 权限名称 | 说明 |
|----------|----------|------|
| xgClassPosMsg:xgClassPosMsg:list | 班级干部信息列表 | 查看班级干部信息列表 |
| xgClassPosMsg:xgClassPosMsg:query | 班级干部信息查询 | 查询班级干部信息详情 |
| xgClassPosMsg:xgClassPosMsg:add | 班级干部信息新增 | 新增班级干部信息 |
| xgClassPosMsg:xgClassPosMsg:edit | 班级干部信息修改 | 修改班级干部信息 |
| xgClassPosMsg:xgClassPosMsg:remove | 班级干部信息删除 | 删除班级干部信息 |
| xgClassPosMsg:xgClassPosMsg:export | 班级干部信息导出 | 导出班级干部信息 |
### 数据权限
- 支持按部门查询班级干部信息
- 通过 `deptId` 参数控制数据权限范围
- 自动包含子部门的数据
## 🚨 错误码说明
| 错误码 | 错误信息 | 说明 |
|--------|----------|------|
| 40001 | 学生不存在 | 指定的学生ID不存在 |
| 40002 | 班级不存在 | 指定的班级ID不存在 |
| 40003 | 职务不存在 | 指定的职务编码不存在 |
| 40004 | 学生已担任该职务 | 学生已经担任该职务 |
| 40005 | 班级干部记录不存在 | 指定的班级干部记录不存在 |
| 40006 | 操作权限不足 | 当前用户没有操作权限 |
| 40007 | 数据校验失败 | 提交的数据不符合规则 |
| 40008 | 用户角色关联失败 | 用户角色关联操作失败 |
## 📋 使用示例
### 1. 查询班级干部信息
```javascript
// 查询指定部门的班级干部信息
const response = await api.get('/xgClassPosMsg/xgClassPosMsg/list', {
params: {
deptId: 101,
pageNum: 1,
pageSize: 10
}
});
console.log(response.data);
```
### 2. 新增班级干部
```javascript
// 新增班级干部信息
const addData = {
classId: 101,
studentId: 1001,
studentNo: "2023001",
studentName: "张三",
positionCode: "monitor",
appointmentDate: "2024-09-01",
termStartDate: "2024-09-01",
termEndDate: "2025-07-01",
termDuration: 10,
isCurrent: 1,
cadreStatus: "1",
appointmentReason: "综合表现优秀",
appointmentMethod: "appointment"
};
const response = await api.post('/xgClassPosMsg/xgClassPosMsg', addData);
```
### 3. 删除班级干部
```javascript
// 删除班级干部信息
const response = await api.delete('/xgClassPosMsg/xgClassPosMsg/1,2,3');
```
## ⚠️ 注意事项
1. **数据一致性**: 班级干部信息与用户角色关联保持一致
2. **权限控制**: 严格按照角色权限进行操作控制
3. **事务管理**: 所有关联操作都在事务中进行
4. **任期管理**: 注意任期时间的合理性和连续性
5. **状态管理**: 正确维护班级干部的任职状态
6. **并发控制**: 防止同时操作导致的数据冲突
7. **日志记录**: 重要操作都会记录详细日志
## 📈 更新日志
| 版本 | 日期 | 更新内容 |
|------|------|----------|
| V1.0.0 | 2025-01-27 | 初始版本,完整的班级干部信息管理API |
| V1.0.1 | 2025-01-27 | 新增用户角色关联功能 |
---
**文档维护**: Cursor AI 辅助
**最后更新**: 2025-01-27
**文档状态**: 已完成
@@ -398,6 +398,122 @@ def run_all_tests(self):
3. **缓存策略**:对频繁查询的字典数据使用 Redis 缓存
4. **接口响应优化**:减少不必要的字段传输
## 📁 文件下载最佳实践 (2025-08-02 新增)
### 核心问题解决
基于OSS文件下载功能的修复经验,总结文件下载相关的最佳实践。
#### 1. 响应头设置规范
**🔴 关键点:二进制文件下载不要设置charset**
```java
// ❌ 错误方式 - 会导致下载失败
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE + "; charset=UTF-8");
// ✅ 正确方式 - 纯二进制类型
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
// ✅ 必需的缓存控制头
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
```
#### 2. Content-Length 正确设置
```java
// ❌ 可能失效的方法引用
storage.download(fileName, response.getOutputStream(), response::setContentLengthLong);
// ✅ 确保安全的设置方式
storage.download(fileName, response.getOutputStream(), contentLength -> {
if (contentLength != null && contentLength > 0) {
response.setContentLengthLong(contentLength);
}
});
```
#### 3. 输出流处理
```java
try {
// 文件下载逻辑
storage.download(fileName, response.getOutputStream(), contentLengthConsumer);
// 🔴 关键:必须手动刷新输出流
response.getOutputStream().flush();
} catch (Exception e) {
throw new ServiceException("文件下载失败: " + e.getMessage());
}
```
#### 4. 完整的文件下载模板
```java
@GetMapping("/download/{fileId}")
public void downloadFile(@PathVariable Long fileId, HttpServletResponse response) {
try {
// 1. 权限验证
if (!hasDownloadPermission(fileId)) {
throw new ServiceException("无权限下载该文件");
}
// 2. 获取文件信息
FileVo fileInfo = fileService.getById(fileId);
if (fileInfo == null) {
throw new ServiceException("文件不存在");
}
// 3. 设置响应头
FileUtils.setAttachmentResponseHeader(response, fileInfo.getOriginalName());
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
// 4. 执行下载
OssClient storage = OssFactory.instance(fileInfo.getService());
storage.download(fileInfo.getFileName(), response.getOutputStream(), contentLength -> {
if (contentLength != null && contentLength > 0) {
response.setContentLengthLong(contentLength);
}
});
// 5. 刷新输出流
response.getOutputStream().flush();
} catch (Exception e) {
log.error("文件下载失败: {}", e.getMessage(), e);
throw new ServiceException("文件下载失败: " + e.getMessage());
}
}
```
### 常见问题排查
#### 问题1:文件传输完成但客户端未收到
- **原因**:Content-Type包含charset或缺少flush()
- **解决**:移除charset,添加手动flush
#### 问题2:下载的文件损坏
- **原因**:Content-Length设置错误
- **解决**:使用lambda表达式确保Content-Length正确设置
#### 问题3:浏览器缓存导致下载异常
- **原因**:缺少缓存控制头
- **解决**:设置no-cache相关响应头
### 测试验证标准
1. **Postman测试**:能正确下载文件
2. **浏览器测试**:点击下载链接正常
3. **文件完整性**:下载文件与原文件MD5一致
4. **不同文件类型**:图片、文档、压缩包都能正常下载
5. **权限控制**:无权限用户被正确拒绝
## 🔄 持续改进
### 规范迭代
@@ -438,5 +554,32 @@ def run_all_tests(self):
**总结**:本最佳实践基于场地预约管理系统的成功开发经验,形成了一套完整的 API 开发标准流程,为后续 API 模块开发提供了可复制的标准模板和质量保证体系。
**维护人员**:开发团队
**最后更新**:2025-07-01
**文档版本**:v1.0
**最后更新**:2025-08-02
**文档版本**:v1.1 (新增文件下载最佳实践)
## 前端开发最佳实践
### 🎯 组件开发规范
#### ImagePreview图片预览组件
- **认证图片加载**: 通过axios获取blob数据,确保认证信息传递
- **内存管理**: 使用`onBeforeUnmount`清理blob URLs,防止内存泄漏
- **缓存优化**: 实现blob URL缓存机制,提升性能
**核心实现**:
```javascript
// 认证图片加载
const getImageBlobUrl = async (imageUrl: string): Promise<string> => {
const response = await request({
url: imageUrl,
method: 'get',
responseType: 'blob'
});
return URL.createObjectURL(response.data);
};
// 生命周期管理
onBeforeUnmount(() => {
blobUrls.value.forEach(url => URL.revokeObjectURL(url));
});
```
@@ -0,0 +1,131 @@
# ImagePreview 组件使用说明
## 概述
ImagePreview 是一个图片预览组件,支持两种模式:
1. **普通模式**:直接使用URL字符串
2. **OSS模式**:通过ossid调用接口获取真实URL
## 功能特性
- ✅ 支持单张和多张图片预览
- ✅ 支持OSS ID模式,自动获取真实URL
- ✅ 内置缓存机制,避免重复请求
- ✅ 加载状态显示
- ✅ 错误状态处理
- ✅ 响应式尺寸设置
- ✅ 图片预览功能
## 组件属性
| 属性名 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| src | String | '' | 图片源,可以是URL或OSS ID |
| width | Number/String | '' | 图片宽度 |
| height | Number/String | '' | 图片高度 |
| isOssId | Boolean | false | 是否为OSS ID模式 |
## 使用示例
### 1. 普通模式(直接使用URL)
```vue
<template>
<ImagePreview
src="https://example.com/image1.jpg,https://example.com/image2.jpg"
:width="200"
:height="150"
/>
</template>
```
### 2. OSS模式(通过ossid获取URL)
```vue
<template>
<ImagePreview
src="123,456,789"
:width="200"
:height="150"
:is-oss-id="true"
/>
</template>
```
### 3. 单张图片
```vue
<template>
<ImagePreview
src="123"
:width="100"
:height="100"
:is-oss-id="true"
/>
</template>
```
## 技术实现
### 核心功能
1. **OSS ID解析**:支持逗号分隔的多个OSS ID
2. **URL缓存**:使用Map缓存已获取的URL,避免重复请求
3. **异步加载**:通过watch监听src变化,异步获取真实URL
4. **错误处理**:完善的错误处理和日志记录
### API调用
组件内部使用 `listByIds` API获取OSS文件信息:
```typescript
// 通过ossid获取真实URL
const getUrlByOssId = async (ossId: string): Promise<string> => {
// 检查缓存
if (urlCache.value.has(ossId)) {
return urlCache.value.get(ossId)!;
}
try {
loading.value = true;
const response = await listByIds(ossId);
const ossList: OssVO[] = response.data;
if (ossList && ossList.length > 0) {
const url = ossList[0].url;
// 缓存结果
urlCache.value.set(ossId, url);
return url;
}
return '';
} catch (error) {
console.error('获取OSS URL失败:', error);
return '';
} finally {
loading.value = false;
}
};
```
### 缓存机制
```typescript
const urlCache = ref<Map<string, string>>(new Map());
```
- 使用Map结构缓存ossid到url的映射
- 避免重复的API调用
- 提升用户体验和性能
## 注意事项
1. **网络请求**:OSS模式下会发起网络请求,确保网络连接正常
2. **权限控制**:确保用户有访问OSS文件的权限
3. **错误处理**:组件内置错误处理,但建议在业务层面也做相应处理
4. **性能优化**:缓存机制已内置,无需额外优化
## 更新记录
- **2025-08-02**:修复组件,支持OSS ID模式,添加缓存机制和加载状态
- 修复了导入错误和computed未定义的问题
- 添加了完整的错误处理和日志记录
@@ -0,0 +1,261 @@
# ImageUpload组件新文件显示修复说明
**更新时间**: 2025-01-27
**版本**: v1.1.0
**修复类型**: Bug修复
## 修复概述
本次修复解决了ImageUpload组件中新上传文件图片不显示的问题,**特别是修复了通过OSS ID获取图片的关键机制**,确保新上传的文件能够与历史文件一样正常显示。
## 问题现象
### 修复前
- 新上传的文件显示为空白或占位符
- 新上传的文件没有经过认证处理
- 新上传的文件缺少必要的属性
- **🔴 关键问题**: 新上传文件直接使用URL,而不是通过OSS ID获取
### 修复后
- ✅ 新上传的文件能正常显示
- ✅ 新上传的文件经过认证的blob URL转换
- ✅ 新上传的文件具有完整的属性
- ✅ 支持占位符显示和加载状态
- ✅ **🔴 新上传文件通过OSS ID获取图片**
## 技术改进
### 1. 🔴 OSS ID获取机制(关键修复)
```javascript
// 新上传文件异步加载处理 - 优先使用OSS ID
const loadPromises = newUploadedFiles.map((item, index) => {
return (async () => {
try {
let blobUrl = '';
if (item.ossId) {
// 🔴 关键修复:通过OSS ID获取预览URL
blobUrl = await getPreviewUrlByOssId(item.ossId);
} else if (item.url) {
// 降级处理:直接URL
blobUrl = await getPreviewImageBlobUrl(item.url);
}
// 更新文件列表
updateFileList(item, blobUrl);
} catch (error) {
// 降级处理:重新通过OSS ID获取
fallbackToOssId(item);
}
})();
});
```
### 2. 占位符显示机制
```javascript
// 占位符项目创建
const placeholderItems = newUploadedFiles.map((item, index) => ({
name: item.name,
url: 'data:image/svg+xml;base64,...', // 加载中占位图片
originalUrl: item.url,
ossId: item.ossId,
isLoaded: false,
isPlaceholder: true
}));
```
### 3. 错误处理和降级机制
- 网络异常时自动降级到原始URL
- 完整的错误日志记录
- 用户友好的错误提示
- **🔴 多级降级策略**: OSS ID → URL → 重新OSS ID → 原始URL
## 🔴 OSS ID获取流程
### 1. 上传成功响应
```javascript
// 服务器返回的数据结构
{
code: 200,
data: {
url: "http://example.com/file.jpg", // 文件URL
fileName: "test.jpg", // 文件名
ossId: "1951584287279034370" // 🔴 关键:OSS ID
}
}
```
### 2. 前端处理流程
```javascript
// 1. 接收上传响应
const uploadItem = {
name: res.data.fileName,
url: res.data.url,
ossId: res.data.ossId // 🔴 保存OSS ID
};
// 2. 通过OSS ID获取文件信息
const ossList = await listByIds(ossId);
// 3. 获取认证的blob URL
const blobUrl = await getPreviewImageBlobUrl(ossList[0].url);
// 4. 更新文件列表显示
fileList[index].url = blobUrl;
```
### 3. 降级处理策略
1. **第一优先级**: 通过OSS ID获取图片
2. **第二优先级**: 直接使用URL获取图片
3. **第三优先级**: 重新尝试OSS ID获取
4. **最终降级**: 使用原始URL
## 使用方法
### 基本用法
```vue
<template>
<ImageUpload v-model="form.images" :limit="5" />
</template>
<script setup>
import { ref } from 'vue';
import ImageUpload from '@/components/ImageUpload/index.vue';
const form = ref({
images: ''
});
</script>
```
### 高级配置
```vue
<template>
<ImageUpload
v-model="form.images"
:limit="10"
:file-size="5"
:file-type="['png', 'jpg', 'jpeg']"
:compress-support="true"
:compress-target-size="300"
/>
</template>
```
## 测试验证
### 测试页面
使用测试页面验证功能:`web/src/views/demo/ImageUploadTest.vue`
### 测试步骤
1. 打开测试页面
2. 上传新图片文件
3. 验证图片是否正常显示
4. 测试图片预览功能
5. 测试删除功能
6. **🔴 验证OSS ID获取机制**
### 验证要点
- ✅ 新上传图片正常显示
- ✅ 历史图片正常显示
- ✅ 混合文件场景正常
- ✅ 图片预览功能正常
- ✅ 删除功能正常
- ✅ 错误处理有效
- ✅ **🔴 新上传文件通过OSS ID获取图片**
## 调试信息
### 控制台日志
组件会在控制台输出详细的调试信息:
```
handleUploadSuccess:res {code: 200, data: {...}}
handleUploadSuccess:file File {...}
添加上传项到uploadList: {name: "test.jpg", url: "...", ossId: "123"}
uploadedSuccessfully called, number: 1, uploadList length: 1
开始处理新上传的文件: [...]
通过OSS ID获取预览URL for 123
获取到的blob URL for 123: blob:http://...
成功更新文件 123
```
### 🔴 OSS ID相关日志
```
通过OSS ID获取预览URL for 1951584287279034370
loadFileImageAsync: 通过OSS ID获取预览URL for 1951584287279034370
降级处理:重新通过OSS ID获取 1951584287279034370
```
### 调试方法
1. 打开浏览器开发者工具
2. 查看Console面板的日志输出
3. 根据日志信息排查问题
4. **🔴 特别关注OSS ID相关的日志**
## 常见问题
### Q1: 新上传的图片仍然不显示?
**A**: 检查控制台是否有错误信息,确认网络连接正常,OSS服务可用。**特别检查OSS ID获取相关的日志**。
### Q2: 图片加载很慢?
**A**: 这是正常的,因为需要经过OSS ID获取和认证的blob URL转换。可以通过网络优化或CDN加速改善。
### Q3: 某些图片无法预览?
**A**: 可能是权限问题或文件损坏,检查OSS文件是否正常,权限是否正确。**确认是否通过OSS ID正确获取**。
### Q4: 如何禁用异步加载?
**A**: 目前不支持禁用,异步加载和OSS ID获取是必需的认证机制。如需优化,可以考虑缓存策略。
### Q5: 🔴 为什么必须通过OSS ID获取?
**A**: OSS ID获取机制确保:
- 文件访问权限验证
- 统一的认证处理
- 与历史文件处理一致性
- 更好的安全性和稳定性
## 性能优化
### 1. 缓存机制
- blob URL缓存避免重复请求
- 预览URL缓存提升响应速度
- **🔴 OSS ID信息缓存**
### 2. 并行处理
- 多文件并行加载
- 异步处理不阻塞UI
- **🔴 OSS ID批量获取**
### 3. 内存管理
- 自动清理blob URL
- 组件卸载时释放资源
- **🔴 OSS ID缓存清理**
## 更新日志
### v1.1.0 (2025-01-27)
- 🐛 修复新上传文件图片不显示问题
- ✨ 添加异步加载机制
- ✨ 添加占位符显示
- ✨ 完善错误处理
- 📝 添加详细调试日志
- 🎨 优化样式和动画
- **🔴 关键修复**: 新上传文件通过OSS ID获取图片
### v1.0.0 (之前版本)
- 基础图片上传功能
- 历史文件显示
- 图片预览功能
## 技术支持
如遇到问题,请:
1. 查看控制台错误信息
2. 检查网络连接状态
3. 验证OSS服务配置
4. 参考测试页面示例
5. **🔴 特别检查OSS ID获取相关日志**
6. 联系技术支持团队
---
**注意**: 本次修复向后兼容,不会影响现有功能的使用。**关键改进是确保新上传文件通过OSS ID获取图片,与历史文件处理机制保持一致**。
@@ -0,0 +1,260 @@
# SQL缓存导出配置使用指南
## 🎯 功能概述
SQL缓存导出配置功能允许您在导出Excel时,通过配置SQL语句来加载数据到缓存进行匹配,实现高性能的数据转换。
## 🚀 核心特性
### 1. 高性能缓存机制
- **静态缓存**:使用`ConcurrentHashMap`存储SQL查询结果
- **预加载机制**:导出前批量预加载所有SQL数据
- **缓存命中优化**:从内存缓存获取数据,避免重复数据库查询
- **线程安全**:支持多用户并发导出
### 2. 灵活的SQL配置
- **自定义SQL**:支持任意SQL查询语句
- **字段映射**:可配置值字段和标签字段
- **动态参数**:支持SQL参数占位符
- **配置管理**:统一的配置注册和管理
### 3. 简单易用的注解
- **@ExcelSqlCacheFormat**:配置SQL缓存转换
- **自动转换**:值到标签的自动转换
- **反向查找**:支持标签到值的反向转换
## 📋 使用步骤
### 1. 注册SQL配置
#### 方式一:通过配置初始化器(推荐)
```java
@Component
public class SqlCacheConfigInitializer implements CommandLineRunner {
@Autowired
private SqlCacheConfigService sqlCacheConfigService;
@Override
public void run(String... args) throws Exception {
// 注册SQL配置
sqlCacheConfigService.registerSqlConfig(
"student_class_info", // SQL键
"SELECT class_id as id, class_name as name FROM xg_class WHERE deleted = 0", // SQL语句
"id", // 值字段
"name" // 标签字段
);
}
}
```
#### 方式二:通过服务直接注册
```java
@Autowired
private SqlCacheConfigService sqlCacheConfigService;
public void registerConfig() {
sqlCacheConfigService.registerSqlConfig(
"college_info",
"SELECT college_id as id, college_name as name FROM xg_college WHERE deleted = 0",
"id",
"name"
);
}
```
### 2. 在VO类中使用注解
```java
@Data
@ExcelIgnoreUnannotated
public class XgStudentMsgExportVo implements Serializable {
/**
* 班级名称 - 使用SQL缓存转换
*/
@ExcelProperty(value = "班级名称", converter = ExcelSqlCacheConvert.class)
@ExcelSqlCacheFormat(
sqlKey = "student_class_info", // 对应注册的SQL键
valueColumn = "id", // 值字段名
labelColumn = "name" // 标签字段名
)
private String className;
/**
* 学院名称 - 使用SQL缓存转换
*/
@ExcelProperty(value = "学院名称", converter = ExcelSqlCacheConvert.class)
@ExcelSqlCacheFormat(
sqlKey = "college_info",
valueColumn = "id",
labelColumn = "name"
)
private String collegeName;
}
```
### 3. 在Controller中预加载缓存
```java
@RestController
public class XgStudentMsgController extends BaseController {
/**
* 预加载导出所需的SQL缓存数据
*/
private void preloadExportSqlData() {
try {
SqlCacheConfigService sqlCacheConfigService = SpringUtils.getBean(SqlCacheConfigService.class);
// 预加载SQL配置
sqlCacheConfigService.preloadSqlData("student_class_info");
sqlCacheConfigService.preloadSqlData("college_info");
sqlCacheConfigService.preloadSqlData("major_info");
log.info("SQL缓存预加载完成");
} catch (Exception e) {
log.warn("SQL缓存预加载失败,将使用默认处理方式", e);
}
}
/**
* 导出方法
*/
@PostMapping("/export")
public void export(HttpServletResponse response) {
// 预加载缓存
preloadExportSqlData();
// 执行导出
List<XgStudentMsgExportVo> list = service.queryExportList();
ExcelUtil.exportExcel(list, "学生信息", XgStudentMsgExportVo.class, response);
}
}
```
## 🔧 配置示例
### 1. 基础查询配置
```java
// 班级信息查询
"student_class_info" -> "SELECT class_id as id, class_name as name FROM xg_class WHERE deleted = 0"
// 学院信息查询
"college_info" -> "SELECT college_id as id, college_name as name FROM xg_college WHERE deleted = 0"
// 专业信息查询
"major_info" -> "SELECT major_id as id, major_name as name FROM xg_major WHERE deleted = 0"
```
### 2. 复杂查询配置
```java
// 带条件的查询
"active_teachers" -> "SELECT user_id as id, nick_name as name FROM sys_user WHERE deleted = 0 AND user_type = '00' AND status = '0'"
// 关联查询
"class_with_teacher" -> """
SELECT c.class_id as id,
CONCAT(c.class_name, ' (', u.nick_name, ')') as name
FROM xg_class c
LEFT JOIN sys_user u ON c.class_teacher_id = u.user_id
WHERE c.deleted = 0
"""
// 聚合查询
"dept_student_count" -> """
SELECT dept_id as id,
CONCAT(dept_name, ' (', COUNT(*), '人)') as name
FROM xg_student_msg
WHERE deleted = 0
GROUP BY dept_id, dept_name
"""
```
### 3. 动态参数查询
```java
// 支持参数的查询(需要特殊处理)
"tenant_college_info" -> "SELECT college_id as id, college_name as name FROM xg_college WHERE deleted = 0 AND tenant_id = ?"
```
## 📊 性能优化
### 1. 缓存策略
- **预加载**:导出前批量加载所有SQL数据
- **静态缓存**:使用`ConcurrentHashMap`避免重复查询
- **内存优化**:合理控制缓存数据量
### 2. SQL优化
- **索引优化**:确保查询字段有合适的索引
- **查询优化**:避免复杂的关联查询
- **数据量控制**:限制单次查询的数据量
### 3. 监控和统计
```java
// 获取缓存统计信息
Map<String, Integer> stats = sqlCacheConfigService.getCacheStats();
log.info("SQL缓存统计: {}", stats);
// 清理缓存
sqlCacheConfigService.clearAllCache();
```
## ⚠️ 注意事项
### 1. SQL安全
- **参数化查询**:避免SQL注入风险
- **权限控制**:确保SQL查询权限合理
- **数据验证**:验证SQL查询结果的正确性
### 2. 缓存管理
- **内存使用**:监控缓存占用的内存
- **缓存更新**:数据变更时及时清理缓存
- **缓存失效**:设置合理的缓存过期策略
### 3. 错误处理
- **异常捕获**:妥善处理SQL执行异常
- **降级策略**:SQL失败时的降级处理
- **日志记录**:详细记录错误信息
## 🔄 与现有功能的对比
### 1. 与字典转换的对比
| 特性 | 字典转换 | SQL缓存转换 |
|------|----------|-------------|
| 数据源 | 字典表 | 任意表 |
| 配置方式 | 字典类型 | SQL语句 |
| 灵活性 | 固定格式 | 高度灵活 |
| 性能 | 预加载缓存 | 预加载缓存 |
| 适用场景 | 标准字典 | 复杂业务数据 |
### 2. 使用场景
- **字典转换**:适用于标准的字典数据(性别、状态等)
- **SQL缓存转换**:适用于复杂的业务数据(班级、学院、专业等)
## 📝 最佳实践
### 1. 配置管理
- 将SQL配置集中管理
- 使用有意义的SQL键名
- 定期检查和优化SQL语句
### 2. 性能优化
- 合理使用索引
- 控制查询数据量
- 监控缓存使用情况
### 3. 错误处理
- 实现降级策略
- 记录详细日志
- 定期检查缓存状态
## 🎉 总结
SQL缓存导出配置功能为Excel导出提供了强大的数据转换能力:
1. **高性能**:通过预加载缓存显著提升导出性能
2. **灵活性**:支持任意SQL查询和字段映射
3. **易用性**:简单的注解配置即可使用
4. **可扩展性**:支持复杂的业务场景
该功能特别适用于需要从多个业务表获取数据进行导出的场景,能够有效解决传统导出方式中的性能瓶颈问题。
@@ -27,8 +27,8 @@
### 1. 导入API和类型
```typescript
import { listColleges, collegeTree, listMajorsByCollegeId } from '@/api/classMsg/college';
import { SysDeptVo } from '@/api/classMsg/types';
import { listColleges, collegeTree, listMajorsByCollegeId } from '@/api/xg/xsjcxx/classmsg/college';
import { SysDeptVo } from '@/api/xg/xsjcxx/classmsg/types';
```
### 2. 定义数据变量
@@ -0,0 +1,215 @@
# 图片上传组件安全改造说明
## 📋 概述
为了配合MinIO安全改造,图片上传组件 `ImageUpload/index.vue` 已经进行了安全升级,支持bucket密码验证和预签名URL机制。
## 🔧 主要改动
### 1. 新增安全功能
- ✅ **Bucket密码验证**: 支持在文件上传时验证bucket访问密码
- ✅ **自定义上传**: 使用 `:http-request="customUpload"` 替代默认上传
- ✅ **密码输入对话框**: 当需要密码验证时,弹出密码输入界面
- ✅ **安全预览**: 文件预览时使用安全机制
### 2. 新增Props参数
```typescript
interface Props {
// ... 原有参数 ...
// bucket访问密码
bucketPassword?: string;
// 是否启用bucket密码验证
enablePasswordValidation?: boolean;
}
```
## 🚀 使用方法
### 基础用法(不启用密码验证)
```vue
<template>
<ImageUpload
v-model="imageList"
:limit="5"
:file-size="10"
:file-type="['png', 'jpg', 'jpeg']"
/>
</template>
```
### 启用密码验证
```vue
<template>
<ImageUpload
v-model="imageList"
:limit="5"
:file-size="10"
:file-type="['png', 'jpg', 'jpeg']"
:enable-password-validation="true"
:bucket-password="bucketPassword"
/>
</template>
<script setup>
import { ref } from 'vue'
const imageList = ref([])
const bucketPassword = ref('your_bucket_password')
</script>
```
### 动态密码验证
```vue
<template>
<ImageUpload
v-model="imageList"
:limit="5"
:file-size="10"
:file-type="['png', 'jpg', 'jpeg']"
:enable-password-validation="true"
/>
</template>
<script setup>
import { ref } from 'vue'
const imageList = ref([])
// 不传入bucketPassword,组件会自动弹出密码输入对话框
</script>
```
## 🔐 密码验证流程
### 1. 自动密码验证
- 当 `enablePasswordValidation=true` 且 `bucketPassword` 有值时
- 组件会自动在请求头中添加 `bucket-password` 参数
- 后端验证密码后允许上传
### 2. 手动密码验证
- 当 `enablePasswordValidation=true` 且 `bucketPassword` 为空时
- 用户选择文件后,弹出密码输入对话框
- 用户输入密码后,组件验证密码并执行上传
## 📝 上传接口变更
### 原有接口
```javascript
// 直接上传到 /resource/oss/upload
const uploadImgUrl = baseUrl + '/resource/oss/upload'
```
### 新安全接口
```javascript
// 使用自定义上传方法
const customUpload = async (options) => {
const formData = new FormData()
formData.append('file', options.file)
const response = await fetch(uploadImgUrl.value, {
method: 'POST',
headers: {
...headers.value,
'bucket-password': bucketPassword || passwordForm.bucketPassword || ''
},
body: formData
})
// 处理响应...
}
```
## 🎯 预览功能升级
### 安全预览
```javascript
const handlePictureCardPreview = async (file) => {
try {
if (file.ossId) {
// 使用安全预览机制
dialogImageUrl.value = file.url // 简化处理,实际应调用安全预览API
} else {
// 直接使用URL预览
dialogImageUrl.value = file.url
}
dialogVisible.value = true
} catch (error) {
console.error('预览失败:', error)
proxy?.$modal.msgError('预览失败')
}
}
```
## ⚠️ 注意事项
### 1. 后端接口要求
- 上传接口需要支持 `bucket-password` 请求头
- 需要验证bucket密码的有效性
- 返回标准的响应格式:`{ code: 200, data: { fileName, url, ossId } }`
### 2. 密码安全
- 密码不会在组件中明文存储
- 密码验证失败时会提示用户重新输入
- 建议使用强密码并定期更换
### 3. 兼容性
- 组件保持向后兼容,不启用密码验证时行为不变
- 原有的文件上传、预览、删除功能正常工作
## 🔄 迁移指南
### 从旧版本迁移
1. **不启用密码验证**: 无需修改,组件自动兼容
2. **启用密码验证**: 添加 `enable-password-validation="true"` 属性
3. **提供密码**: 通过 `bucket-password` 属性或对话框输入
### 示例迁移
```vue
<!-- 旧版本 -->
<ImageUpload v-model="images" />
<!-- 新版本(启用密码验证) -->
<ImageUpload
v-model="images"
:enable-password-validation="true"
:bucket-password="bucketPassword"
/>
```
## 🧪 测试建议
### 1. 功能测试
- [ ] 基础文件上传功能正常
- [ ] 密码验证功能正常
- [ ] 密码错误时提示正确
- [ ] 预览功能正常
- [ ] 删除功能正常
### 2. 安全测试
- [ ] 无密码时无法上传
- [ ] 错误密码时无法上传
- [ ] 密码验证失败时提示正确
- [ ] 密码不会在控制台泄露
### 3. 兼容性测试
- [ ] 不启用密码验证时功能正常
- [ ] 原有页面无需修改即可使用
- [ ] 文件格式限制正常
- [ ] 文件大小限制正常
## 📚 相关文档
- [MinIO安全改造方案](../中间件配置/minio-security-solution.md)
- [Bucket密码管理API](../API/用户认证登录API.md)
- [文件访问安全API](../API/API文档基础规范.md)
---
**更新时间**: 2025-01-27
**版本**: v2.0.0
**维护者**: 开发团队
@@ -0,0 +1,221 @@
# 图片上传认证问题解决方案
## 🚨 问题描述
调用 `http://localhost/dev-api/resource/oss/upload` 接口时,返回401认证失败错误:
```json
{
"code": 401,
"msg": "认证失败,无法访问系统资源",
"data": null
}
```
## 🔍 问题分析
### 1. 认证头缺失
根据项目规范,上传接口需要以下认证头:
| 字段名 | 必填 | 说明 | 示例值 |
|--------|------|------|--------|
| Authorization | 是 | Bearer + Token | "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." |
| clientid | 是 | 客户端ID | "e5cd7e4891bf95d1d19206ce24a7b32e" |
| Content-Language | 是 | 语言设置 | "zh-CN,zh;q=0.9,en;q=0.8" |
| referer | 是 | 来源页面 | "http://localhost/login?redirect=%252Findex" |
### 2. 当前问题
- ❌ 缺少 `clientid` 字段
- ❌ 缺少 `Content-Language` 字段
- ❌ 缺少 `referer` 字段
- ❌ Token格式可能不正确
## 🔧 解决方案
### 1. 修复Headers设置
在 `ImageUpload/index.vue` 中,已修复headers设置:
```typescript
// 获取token函数
const getToken = () => {
return localStorage.getItem('token') || '';
};
// 获取语言设置
const getLanguage = () => {
return localStorage.getItem('language') || 'zh-CN';
};
// 构建完整的请求头
const requestHeaders = {
'Authorization': 'Bearer ' + getToken(),
'clientid': import.meta.env.VITE_APP_CLIENT_ID || 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Content-Language': getLanguage(),
'referer': window.location.origin + '/login?redirect=' + encodeURIComponent(window.location.pathname),
'bucket-password': props.bucketPassword || passwordForm.value.bucketPassword || ''
};
```
### 2. 验证Token有效性
确保用户已登录且Token有效:
```javascript
// 检查Token是否存在
const token = localStorage.getItem('token');
if (!token) {
console.error('Token不存在,请先登录');
// 跳转到登录页面
window.location.href = '/login';
}
```
### 3. 测试认证信息
在浏览器控制台中运行以下代码测试认证信息:
```javascript
// 检查认证信息
function checkAuth() {
const token = localStorage.getItem('token');
const headers = {
'Authorization': 'Bearer ' + token,
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Content-Language': 'zh-CN',
'referer': window.location.origin + '/login?redirect=' + encodeURIComponent(window.location.pathname)
};
console.log('Token:', token);
console.log('Headers:', headers);
return headers;
}
// 测试上传接口
async function testUpload() {
const headers = checkAuth();
try {
const response = await fetch('/dev-api/resource/oss/upload', {
method: 'POST',
headers: headers,
body: new FormData()
});
const result = await response.json();
console.log('测试结果:', result);
return result;
} catch (error) {
console.error('测试失败:', error);
return null;
}
}
// 运行测试
testUpload();
```
## 🧪 调试步骤
### 步骤1:检查Token
```javascript
// 在浏览器控制台运行
console.log('Token:', localStorage.getItem('token'));
```
### 步骤2:检查Headers
```javascript
// 在浏览器控制台运行
const headers = {
'Authorization': 'Bearer ' + localStorage.getItem('token'),
'clientid': 'e5cd7e4891bf95d1d19206ce24a7b32e',
'Content-Language': 'zh-CN',
'referer': window.location.origin + '/login?redirect=' + encodeURIComponent(window.location.pathname)
};
console.log('Headers:', headers);
```
### 步骤3:测试接口
```javascript
// 在浏览器控制台运行
fetch('/dev-api/resource/oss/upload', {
method: 'POST',
headers: headers,
body: new FormData()
}).then(response => response.json()).then(console.log);
```
## ⚠️ 常见问题
### 1. Token过期
**症状**: 401认证失败
**解决**: 重新登录获取新Token
### 2. clientid错误
**症状**: 401认证失败
**解决**: 检查环境变量 `VITE_APP_CLIENT_ID` 或使用默认值
### 3. 缺少必需字段
**症状**: 401认证失败
**解决**: 确保包含所有必需的认证头字段
### 4. 跨域问题
**症状**: CORS错误
**解决**: 检查后端CORS配置
## 🔄 验证修复
### 1. 功能测试
- [ ] 用户已登录且Token有效
- [ ] Headers包含所有必需字段
- [ ] 上传接口返回200状态码
- [ ] 文件上传成功
### 2. 安全测试
- [ ] 未登录用户无法上传
- [ ] 过期Token无法上传
- [ ] 错误clientid无法上传
## 📝 使用示例
### 正确的Headers示例
```javascript
{
"Authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
"clientid": "e5cd7e4891bf95d1d19206ce24a7b32e",
"Content-Language": "zh-CN",
"referer": "http://localhost/login?redirect=%2Findex",
"bucket-password": "your_bucket_password"
}
```
### 错误示例
```javascript
// ❌ 缺少Bearer前缀
"Authorization": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..."
// ❌ 缺少clientid
{
"Authorization": "Bearer ...",
"Content-Language": "zh-CN"
}
// ❌ 缺少必需字段
{
"Authorization": "Bearer ..."
}
```
## 🔗 相关文档
- [API文档基础规范](../API/API文档基础规范.md)
- [图片上传组件安全改造说明](./图片上传组件安全改造说明.md)
- [MinIO安全改造方案](../中间件配置/minio-security-solution.md)
---
**更新时间**: 2025-01-27
**版本**: v1.0.0
**维护者**: 开发团队
@@ -0,0 +1,257 @@
# 🚀 文件下载缓存机制说明
## 📋 功能概述
为了提高文件下载性能,减少对MinIO存储的重复请求,系统实现了基于内存的文件缓存机制。该机制具有以下特性:
### ✨ 核心特性
- **LRU淘汰策略**: 当缓存空间不足时,自动淘汰最久未访问的文件
- **智能大小控制**: 只缓存小于50MB的文件,避免内存溢出
- **过期时间管理**: 缓存条目具有30分钟的有效期
- **并发安全**: 使用读写锁保证多线程环境下的数据一致性
- **统计监控**: 提供缓存命中率、大小等统计信息
- **配置化管理**: 所有缓存参数可通过配置文件调整
## 🔧 配置参数
### 基础配置
在 `application.yml` 或 `application-dev.yml` 中添加以下配置:
```yaml
system:
file-cache:
# 是否启用文件缓存
enabled: true
# 最大缓存文件数量
max-cache-size: 100
# 最大缓存大小(字节)- 500MB
max-cache-size-bytes: 524288000
# 缓存过期时间(毫秒)- 30分钟
expire-time-ms: 1800000
# 最大缓存单个文件大小(字节)- 50MB
max-single-file-size: 52428800
# LRU淘汰比例(百分比)
evict-ratio: 20
# 是否启用缓存统计
enable-stats: true
# 缓存清理间隔(毫秒)- 5分钟
cleanup-interval-ms: 300000
```
### 参数说明
| 参数名 | 默认值 | 说明 |
|--------|--------|------|
| `enabled` | `true` | 是否启用文件缓存功能 |
| `max-cache-size` | `100` | 缓存的最大文件数量 |
| `max-cache-size-bytes` | `524288000` | 缓存的最大总大小(500MB) |
| `expire-time-ms` | `1800000` | 缓存条目的过期时间(30分钟) |
| `max-single-file-size` | `52428800` | 单个文件的最大缓存大小(50MB) |
| `evict-ratio` | `20` | LRU淘汰时移除的条目比例(20%) |
| `enable-stats` | `true` | 是否启用缓存统计功能 |
| `cleanup-interval-ms` | `300000` | 自动清理过期条目的间隔(5分钟) |
## 🚀 使用方法
### 1. 文件下载
缓存机制对用户透明,正常使用文件下载接口即可:
```http
GET /resource/oss/download/{ossId}
```
系统会自动:
1. 检查缓存中是否存在该文件
2. 如果存在且未过期,直接从缓存返回
3. 如果不存在或已过期,从MinIO下载并缓存
### 2. 缓存管理API
#### 获取缓存统计信息
```http
GET /resource/oss/cache/stats
```
返回示例:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"cacheSize": 25,
"currentCacheSizeBytes": 104857600,
"maxCacheSizeBytes": 524288000,
"cacheHits": 150,
"cacheMisses": 50,
"hitRate": "75.00%"
}
}
```
#### 清空缓存
```http
DELETE /resource/oss/cache/clear
```
#### 移除指定文件缓存
```http
DELETE /resource/oss/cache/{ossId}
```
## 📊 性能优化
### 缓存命中率优化
- **增加缓存大小**: 对于内存充足的环境,适当增加 `max-cache-size-bytes`
- **调整文件数量**: 对于小文件较多的场景,增加 `max-cache-size`
- **优化过期时间**: 根据文件访问频率调整 `expire-time-ms`
### 内存使用优化
- **控制单个文件大小**: 根据实际情况调整 `max-single-file-size`
- **调整淘汰策略**: 通过 `evict-ratio` 控制淘汰比例
- **定期清理**: 设置合适的 `cleanup-interval-ms`
### 监控指标
- **缓存命中率**: 目标 > 70%
- **内存使用率**: 监控缓存占用的内存大小
- **响应时间**: 缓存命中时的下载速度应显著提升
## 🔍 技术实现
### 核心组件
#### 1. 缓存条目类 (CacheEntry)
```java
private class CacheEntry {
private final byte[] data; // 文件数据
private final long size; // 文件大小
private final long createTime; // 创建时间
private final long lastAccessTime; // 最后访问时间
private final String contentType; // 内容类型
}
```
#### 2. LRU淘汰策略
```java
private void evictLRUEntries() {
// 按最后访问时间排序
List<Map.Entry<Long, CacheEntry>> entries = new ArrayList<>(fileCache.entrySet());
entries.sort((e1, e2) -> Long.compare(e1.getValue().getLastAccessTime(), e2.getValue().getLastAccessTime()));
// 淘汰最旧的20%条目
int evictCount = Math.max(1, entries.size() / 5);
for (int i = 0; i < evictCount && i < entries.size(); i++) {
Map.Entry<Long, CacheEntry> entry = entries.get(i);
fileCache.remove(entry.getKey());
currentCacheSize.addAndGet(-entry.getValue().getSize());
}
}
```
#### 3. 并发安全
- 使用 `ConcurrentHashMap` 存储缓存数据
- 使用 `ReentrantReadWriteLock` 保证读写安全
- 使用 `AtomicLong` 统计缓存大小和命中率
### 工作流程
1. **文件下载请求**
```
用户请求 → 权限验证 → 检查缓存 → 返回文件
```
2. **缓存命中流程**
```
检查缓存 → 验证过期 → 直接返回 → 更新统计
```
3. **缓存未命中流程**
```
检查缓存 → 从MinIO下载 → 添加到缓存 → 返回文件
```
4. **缓存管理流程**
```
检查大小限制 → LRU淘汰 → 清理过期 → 添加新条目
```
## ⚠️ 注意事项
### 1. 内存管理
- 缓存会占用JVM堆内存,需要合理配置缓存大小
- 建议监控应用的内存使用情况
- 大文件不会被缓存,避免内存溢出
### 2. 数据一致性
- 缓存的文件数据可能与MinIO中的文件不同步
- 如果需要最新数据,可以手动清除缓存
- 建议在文件更新后清除相关缓存
### 3. 性能考虑
- 缓存命中时响应速度显著提升
- 缓存未命中时会有轻微的性能开销(用于缓存数据)
- 建议根据实际访问模式优化缓存参数
### 4. 安全考虑
- 缓存数据存储在内存中,应用重启后会丢失
- 敏感文件建议设置较短的过期时间
- 可以通过API手动管理缓存
## 🔧 故障排查
### 常见问题
1. **缓存命中率低**
- 检查缓存大小配置是否合理
- 分析文件访问模式
- 调整过期时间
2. **内存使用过高**
- 减少缓存大小限制
- 降低单个文件大小限制
- 增加淘汰比例
3. **缓存不生效**
- 检查 `enabled` 配置是否为 `true`
- 确认文件大小是否超过限制
- 查看日志中的缓存相关信息
### 监控建议
1. **定期检查缓存统计**
```bash
curl -X GET "http://localhost:8080/resource/oss/cache/stats"
```
2. **监控应用内存使用**
```bash
jstat -gc <pid> 1000
```
3. **查看缓存相关日志**
```bash
tail -f logs/application.log | grep "cache"
```
## 📈 性能测试
### 测试场景
1. **首次下载**: 从MinIO下载并缓存
2. **缓存命中**: 从内存直接返回
3. **并发下载**: 多用户同时下载同一文件
4. **大文件下载**: 超过缓存大小限制的文件
### 预期性能提升
- **缓存命中时**: 响应时间减少 80-90%
- **并发访问**: 显著减少MinIO压力
- **整体性能**: 平均下载速度提升 60-80%
---
**📝 维护说明**: 本文档基于文件缓存机制的实现,如需修改缓存策略或参数,请同步更新相关代码和配置。