Compare commits

...

8 Commits

13
.gitignore vendored

@ -0,0 +1,13 @@
# 忽略 Maven/Gradle 构建产物
target/
build/
# 忽略 IDE 生成的配置文件(以 IntelliJ 为例)
.idea/
*.iml
*.iws
*.ipr
# 忽略系统自动生成的文件
.DS_Store
Thumbs.db

@ -1,267 +1,533 @@
# 学生管理系统 - 后端说明文档
## 一、项目简介
本项目是一个基于 **Spring Boot + MyBatis-Plus** 开发的学生管理系统后端,提供班级管理、学生信息管理等核心功能,支持分页查询、条件筛选、增删改查等操作,前后端分离架构,适配前端 Vue 项目。
## 二、技术栈
| 技术 | 版本 | 说明 |
| ----------------- | ------ | ---------------------------------- |
| Spring Boot | 2.7.x | 快速开发框架 |
| MyBatis-Plus | 3.5.x | MyBatis 增强工具 |
| MySQL | 8.0 | 关系型数据库 |
| Lombok | 1.18.x | 简化 Java 代码getter/setter 等) |
| Spring Validation | 2.7.x | 参数校验(后续优化用) |
## 三、项目结构
```plaintext
src/
├── main/
│ ├── java/
│ │ └── com/example/studentmanagement/
│ │ ├── StudentManagementApplication.java // 启动类
│ │ ├── common/ // 公共类(后续优化用:统一响应、状态码)
│ │ ├── controller/ // 控制器(接口层)
│ │ │ ├── ClassController.java // 班级管理接口
│ │ │ └── StudentController.java // 学生管理接口
│ │ ├── dto/ // 数据传输对象(后续优化用:接收前端参数)
│ │ ├── entity/ // 实体类(对应数据库表)
│ │ │ ├── GradeClass.java // 班级实体
│ │ │ └── Student.java // 学生实体
│ │ ├── mapper/ // Mapper接口MyBatis-Plus
│ │ │ ├── ClassMapper.java
│ │ │ └── StudentMapper.java
│ │ ├── service/ // 服务层
│ │ │ ├── ClassService.java // 班级服务接口
│ │ │ ├── StudentService.java // 学生服务接口
│ │ │ └── impl/ // 服务实现类
│ │ └── config/ // 配置类
│ │ └── MyBatisConfig.java // MyBatis-Plus配置分页插件等
│ └── resources/
│ ├── application.properties // 配置文件(数据库、端口等)
│ └── mapper/ // MyBatis XML映射文件若有
└── test/ // 测试类(可选)
```
## 四、核心功能说明
### 1. 班级管理
- 查询班级列表(按年级筛选、模糊查询班级名称)
- 新增班级
- 编辑班级信息
- 删除班级(校验班级下是否有学生,有则禁止删除)
- 检查班级是否有学生(用于删除前校验)
### 2. 学生管理
- 分页查询学生列表(支持年级、班级、姓名模糊筛选)
- 新增学生
- 编辑学生信息
- 批量删除学生
## 五、接口文档(当前版本)
### 1. 班级管理接口
| 接口地址 | 请求方式 | 参数说明 | 返回结果 |
| ---------------------- | -------- | ------------------------------------------------------ | -------------------------------------- |
| /api/class/list | GET | grade年级必填、name班级名称可选 | 班级列表List<GradeClass> |
| /api/class/add | POST | name班级名称、grade年级、headTeacher班主任 | 新增结果(布尔值) |
| /api/class/edit | PUT | id班级 ID必填、name、grade、headTeacher | 编辑结果(布尔值) |
| /api/class/delete/{id} | DELETE | id班级 ID路径参数 | 删除结果(布尔值) |
| /api/class/check | GET | classId班级 ID必填 | 是否有学生({hasStudent: true/false} |
### 2. 学生管理接口
| 接口地址 | 请求方式 | 参数说明 | 返回结果 |
| ------------------------- | -------- | ------------------------------------------------------------ | -------------------------------------------- |
| /api/student/list | GET | page页码必填、size每页条数必填、grade年级、classId班级 ID、name学生姓名 | 分页学生列表({list: [...], total: 总条数} |
| /api/student/add | POST | name、age、gender、address、grade、classId | 新增结果(布尔值) |
| /api/student/edit | PUT | id学生 ID必填、name、age、gender、address、grade、classId | 编辑结果(布尔值) |
| /api/student/delete/{ids} | DELETE | ids学生 ID 逗号分隔,如 1,2,3 | 删除结果(布尔值) |
## 六、配置说明application.properties
```properties
# 服务器端口默认8080若冲突可修改
server.port=8080
# 数据库连接配置需修改为你的MySQL信息
spring.datasource.url=jdbc:mysql://localhost:3306/student_management?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root # 你的MySQL用户名
spring.datasource.password=123456 # 你的MySQL密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis-Plus配置
# 实体类别名扫描包对应entity目录
mybatis-plus.type-aliases-package=com.example.studentmanagement.entity
# XML映射文件路径若有
mybatis-plus.mapper-locations=classpath:mapper/*.xml
# 开启驼峰命名转换如数据库字段class_id -> 实体类classId
mybatis-plus.configuration.map-underscore-to-camel-case=true
```
## 七、使用说明
### 1. 环境准备
- JDK 8+
- MySQL 8.0
- Maven 3.6+(或使用 IDEA 自带 Maven
### 2. 数据库初始化
1. 创建数据库 student_management
```sql
CREATE DATABASE IF NOT EXISTS student_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
2. 执行数据库脚本创建班级表class和学生表student
```sql
-- 班级表
CREATE TABLE `class` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '班级ID',
`name` varchar(50) NOT NULL COMMENT '班级名称',
`grade` int NOT NULL COMMENT '年级',
`head_teacher` varchar(50) DEFAULT NULL COMMENT '班主任',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='班级表';
-- 学生表关联班级表id
CREATE TABLE `student` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '学生ID',
`name` varchar(50) NOT NULL COMMENT '姓名',
`age` int DEFAULT NULL COMMENT '年龄',
`gender` int DEFAULT NULL COMMENT '性别1-男2-女)',
`address` varchar(255) DEFAULT NULL COMMENT '家庭住址',
`grade` int NOT NULL COMMENT '年级',
`class_id` bigint DEFAULT NULL COMMENT '班级ID关联class表id',
PRIMARY KEY (`id`),
KEY `idx_class_id` (`class_id`),
CONSTRAINT `fk_student_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
```
3. 执行测试数据脚本(批量插入班级和学生数据)
```sql
-- 切换到你的数据库(请确保已创建 student_management 数据库)
USE student_management;
-- 清空现有数据(可选,首次执行可跳过,避免重复插入)
-- TRUNCATE TABLE student;
-- TRUNCATE TABLE class;
-- 1. 插入班级数据3个年级每个年级3个班共9个班级
INSERT INTO class (name, grade, head_teacher) VALUES
-- 一年级班级
('一年级一班', 1, '王老师'),
('一年级二班', 1, '李老师'),
('一年级三班', 1, '张老师'),
-- 二年级班级
('二年级一班', 2, '赵老师'),
('二年级二班', 2, '孙老师'),
('二年级三班', 2, '周老师'),
-- 三年级班级
('三年级一班', 3, '吴老师'),
('三年级二班', 3, '郑老师'),
('三年级三班', 3, '钱老师');
-- 2. 插入学生数据每个班级5名学生共45名学生关联正确的 class_id
INSERT INTO student (name, age, gender, address, grade, class_id) VALUES
-- 一年级一班class_id=1
('张三', 6, 1, '北京市海淀区中关村大街', 1, 1),
('李四', 7, 1, '北京市朝阳区建国路', 1, 1),
('王五', 6, 2, '北京市西城区金融街', 1, 1),
('赵六', 7, 2, '北京市东城区王府井大街', 1, 1),
('孙七', 6, 1, '北京市丰台区马家堡东路', 1, 1),
-- 一年级二班class_id=2
('周八', 6, 1, '上海市黄浦区南京东路', 1, 2),
('吴九', 7, 2, '上海市徐汇区淮海中路', 1, 2),
('郑十', 6, 1, '上海市静安区静安寺路', 1, 2),
('钱十一', 7, 2, '上海市长宁区虹桥路', 1, 2),
('冯十二', 6, 1, '上海市普陀区中山北路', 1, 2),
-- 一年级三班class_id=3
('陈十三', 6, 2, '广州市天河区天河路', 1, 3),
('褚十四', 7, 1, '广州市越秀区北京路', 1, 3),
('卫十五', 6, 2, '广州市海珠区江南大道', 1, 3),
('蒋十六', 7, 1, '广州市荔湾区上下九路', 1, 3),
('沈十七', 6, 2, '广州市白云区机场路', 1, 3),
-- 二年级一班class_id=4
('韩十八', 7, 1, '深圳市福田区深南中路', 2, 4),
('杨十九', 8, 2, '深圳市南山区科技园', 2, 4),
('朱二十', 7, 1, '深圳市罗湖区东门步行街', 2, 4),
('秦二一', 8, 2, '深圳市盐田区沙头角', 2, 4),
('尤二二', 7, 1, '深圳市宝安区宝安大道', 2, 4),
-- 二年级二班class_id=5
('许二三', 7, 2, '杭州市西湖区西湖大道', 2, 5),
('何二四', 8, 1, '杭州市上城区解放路', 2, 5),
('吕二五', 7, 2, '杭州市拱墅区武林路', 2, 5),
('施二六', 8, 1, '杭州市滨江区江南大道', 2, 5),
('张二七', 7, 2, '杭州市萧山区市心路', 2, 5),
-- 二年级三班class_id=6
('孔二八', 7, 1, '成都市锦江区春熙路', 2, 6),
('曹二九', 8, 2, '成都市青羊区宽窄巷子', 2, 6),
('严三十', 7, 1, '成都市金牛区天府大道', 2, 6),
('华三一', 8, 2, '成都市武侯区武侯祠大街', 2, 6),
('金三二', 7, 1, '成都市成华区建设路', 2, 6),
-- 三年级一班class_id=7
('魏三三', 8, 2, '武汉市武昌区东湖路', 3, 7),
('陶三四', 9, 1, '武汉市汉口区中山大道', 3, 7),
('姜三五', 8, 2, '武汉市汉阳古琴台路', 3, 7),
('戚三六', 9, 1, '武汉市洪山区光谷广场', 3, 7),
('谢三七', 8, 2, '武汉市青山区和平大道', 3, 7),
-- 三年级二班class_id=8
('邹三八', 8, 1, '西安市雁塔区大雁塔路', 3, 8),
('喻三九', 9, 2, '西安市碑林区南大街', 3, 8),
('柏四十', 8, 1, '西安市莲湖区北大街', 3, 8),
('水四一', 9, 2, '西安市未央区未央路', 3, 8),
('窦四二', 8, 1, '西安市新城区解放路', 3, 8),
-- 三年级三班class_id=9
('章四三', 8, 2, '南京市鼓楼区中山路', 3, 9),
('云四四', 9, 1, '南京市玄武区玄武湖路', 3, 9),
('苏四五', 8, 2, '南京市秦淮区夫子庙路', 3, 9),
('潘四六', 9, 1, '南京市建邺区河西大街', 3, 9),
('葛四七', 8, 2, '南京市雨花台区雨花大道', 3, 9);
```
### 3. 项目启动
1. 用 IDEA 打开项目,等待 Maven 依赖加载完成。
2. 修改 `application.properties` 中的数据库连接信息(用户名、密码)。
3. 运行 `StudentManagementApplication.java``main` 方法,启动项目。
4. 启动成功后,访问 `http://localhost:8080/api/class/list?grade=1`,若返回班级列表则表示运行正常。
## 八、后续优化方向
1. **参数校验**:引入 `Spring Validation`,用注解(如 `@Min`、`@NotBlank`)替代手动参数判断。
2. **统一响应格式**:创建 `ApiResult` 统一返回结构(状态码、提示信息、数据),替代 `HashMap`
3. **DTO/VO 分离**:用 DTO数据传输对象接收前端参数VO视图对象封装返回数据隐藏后端实体细节。
4. **全局异常处理**:用 `@RestControllerAdvice` 捕获异常,统一返回错误信息。
5. **接口文档自动生成**:引入 Swagger/knife4j自动生成接口文档方便前后端联调。
6. **权限控制**:引入 Spring Security 或 Shiro实现用户登录、权限校验。
## 九、注意事项
1. 数据库表名、字段名采用下划线命名实体类采用驼峰命名MyBatis-Plus 会自动转换(需开启 `map-underscore-to-camel-case`)。
2. 分页功能依赖 MyBatis-Plus 分页插件,已在 `MyBatisConfig.java` 中配置,若分页无效需检查该配置。
3. 前端请求需配置代理(如 Vite 代理),将 `/api` 前缀转发到后端地址(`http://localhost:8080`),避免跨域问题。
# 学生管理系统 - 后端说明文档
## 一、项目简介
本项目是一个基于 **Spring Boot + MyBatis-Plus** 开发的学生管理系统后端,提供班级管理、学生信息管理等核心功能,支持分页查询、条件筛选、增删改查等操作,前后端分离架构,适配前端 Vue 项目。
## 二、技术栈
| 技术 | 版本 | 说明 |
| ----------------- | ------ | ---------------------------------- |
| Spring Boot | 2.7.x | 快速开发框架 |
| MyBatis-Plus | 3.5.x | MyBatis 增强工具 |
| MySQL | 8.0 | 关系型数据库 |
| Lombok | 1.18.x | 简化 Java 代码getter/setter 等) |
| Spring Validation | 2.7.x | 参数校验(后续优化用) |
## 三、项目结构
```plaintext
src/
├── main/
│ ├── java/
│ │ └── com/example/studentmanagement/
│ │ ├── StudentManagementApplication.java // 启动类
│ │ ├── common/ // 公共类(后续优化用:统一响应、状态码)
│ │ ├── controller/ // 控制器(接口层)
│ │ │ ├── ClassController.java // 班级管理接口
│ │ │ └── StudentController.java // 学生管理接口
│ │ ├── dto/ // 数据传输对象(后续优化用:接收前端参数)
│ │ ├── entity/ // 实体类(对应数据库表)
│ │ │ ├── GradeClass.java // 班级实体
│ │ │ └── Student.java // 学生实体
│ │ ├── mapper/ // Mapper接口MyBatis-Plus
│ │ │ ├── ClassMapper.java
│ │ │ └── StudentMapper.java
│ │ ├── service/ // 服务层
│ │ │ ├── ClassService.java // 班级服务接口
│ │ │ ├── StudentService.java // 学生服务接口
│ │ │ └── impl/ // 服务实现类
│ │ └── config/ // 配置类
│ │ └── MyBatisConfig.java // MyBatis-Plus配置分页插件等
│ └── resources/
│ ├── application.properties // 配置文件(数据库、端口等)
│ └── mapper/ // MyBatis XML映射文件若有
└── test/ // 测试类(可选)
```
## 四、核心功能说明
### 1. 班级管理
- 查询班级列表(按年级筛选、模糊查询班级名称)
- 新增班级
- 编辑班级信息
- 删除班级(校验班级下是否有学生,有则禁止删除)
- 检查班级是否有学生(用于删除前校验)
### 2. 学生管理
- 分页查询学生列表(支持年级、班级、姓名模糊筛选)
- 新增学生
- 编辑学生信息
- 批量删除学生
## 五、接口文档(当前版本)
### 1. 班级管理接口
| 接口地址 | 请求方式 | 参数说明 | 返回结果 |
| ---------------------- | -------- | ------------------------------------------------------ | -------------------------------------- |
| /api/class/list | GET | grade年级必填、name班级名称可选 | 班级列表List<GradeClass> |
| /api/class/add | POST | name班级名称、grade年级、headTeacher班主任 | 新增结果(布尔值) |
| /api/class/edit | PUT | id班级 ID必填、name、grade、headTeacher | 编辑结果(布尔值) |
| /api/class/delete/{id} | DELETE | id班级 ID路径参数 | 删除结果(布尔值) |
| /api/class/check | GET | classId班级 ID必填 | 是否有学生({hasStudent: true/false} |
### 2. 学生管理接口
| 接口地址 | 请求方式 | 参数说明 | 返回结果 |
| ------------------------- | -------- | ------------------------------------------------------------ | -------------------------------------------- |
| /api/student/list | GET | page页码必填、size每页条数必填、grade年级、classId班级 ID、name学生姓名 | 分页学生列表({list: [...], total: 总条数} |
| /api/student/add | POST | name、age、gender、address、grade、classId | 新增结果(布尔值) |
| /api/student/edit | PUT | id学生 ID必填、name、age、gender、address、grade、classId | 编辑结果(布尔值) |
| /api/student/delete/{ids} | DELETE | ids学生 ID 逗号分隔,如 1,2,3 | 删除结果(布尔值) |
## 六、配置说明application.properties
```properties
# 服务器端口默认8080若冲突可修改
server.port=8080
# 数据库连接配置需修改为你的MySQL信息
spring.datasource.url=jdbc:mysql://localhost:3306/student_management?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root # 你的MySQL用户名
spring.datasource.password=123456 # 你的MySQL密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis-Plus配置
# 实体类别名扫描包对应entity目录
mybatis-plus.type-aliases-package=com.example.studentmanagement.entity
# XML映射文件路径若有
mybatis-plus.mapper-locations=classpath:mapper/*.xml
# 开启驼峰命名转换如数据库字段class_id -> 实体类classId
mybatis-plus.configuration.map-underscore-to-camel-case=true
```
## 七、使用说明
### 1. 环境准备
- JDK 8+
- MySQL 8.0
- Maven 3.6+(或使用 IDEA 自带 Maven
### 2. 数据库初始化
1. 创建数据库 student_management
```sql
CREATE DATABASE IF NOT EXISTS student_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
2. 执行数据库脚本创建班级表class和学生表student
```sql
-- 班级表
CREATE TABLE `class` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '班级ID',
`name` varchar(50) NOT NULL COMMENT '班级名称',
`grade` int NOT NULL COMMENT '年级',
`head_teacher` varchar(50) DEFAULT NULL COMMENT '班主任',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='班级表';
-- 学生表关联班级表id
CREATE TABLE `student` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '学生ID',
`name` varchar(50) NOT NULL COMMENT '姓名',
`age` int DEFAULT NULL COMMENT '年龄',
`gender` int DEFAULT NULL COMMENT '性别1-男2-女)',
`address` varchar(255) DEFAULT NULL COMMENT '家庭住址',
`grade` int NOT NULL COMMENT '年级',
`class_id` bigint DEFAULT NULL COMMENT '班级ID关联class表id',
PRIMARY KEY (`id`),
KEY `idx_class_id` (`class_id`),
CONSTRAINT `fk_student_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
```
3. 执行测试数据脚本(批量插入班级和学生数据)
```sql
-- 切换到你的数据库(请确保已创建 student_management 数据库)
USE student_management;
-- 清空现有数据(可选,首次执行可跳过,避免重复插入)
-- TRUNCATE TABLE student;
-- TRUNCATE TABLE class;
-- 1. 插入班级数据3个年级每个年级3个班共9个班级
INSERT INTO class (name, grade, head_teacher) VALUES
-- 一年级班级
('一年级一班', 1, '王老师'),
('一年级二班', 1, '李老师'),
('一年级三班', 1, '张老师'),
-- 二年级班级
('二年级一班', 2, '赵老师'),
('二年级二班', 2, '孙老师'),
('二年级三班', 2, '周老师'),
-- 三年级班级
('三年级一班', 3, '吴老师'),
('三年级二班', 3, '郑老师'),
('三年级三班', 3, '钱老师');
-- 2. 插入学生数据每个班级5名学生共45名学生关联正确的 class_id
INSERT INTO student (name, age, gender, address, grade, class_id) VALUES
-- 一年级一班class_id=1
('张三', 6, 1, '北京市海淀区中关村大街', 1, 1),
('李四', 7, 1, '北京市朝阳区建国路', 1, 1),
('王五', 6, 2, '北京市西城区金融街', 1, 1),
('赵六', 7, 2, '北京市东城区王府井大街', 1, 1),
('孙七', 6, 1, '北京市丰台区马家堡东路', 1, 1),
-- 一年级二班class_id=2
('周八', 6, 1, '上海市黄浦区南京东路', 1, 2),
('吴九', 7, 2, '上海市徐汇区淮海中路', 1, 2),
('郑十', 6, 1, '上海市静安区静安寺路', 1, 2),
('钱十一', 7, 2, '上海市长宁区虹桥路', 1, 2),
('冯十二', 6, 1, '上海市普陀区中山北路', 1, 2),
-- 一年级三班class_id=3
('陈十三', 6, 2, '广州市天河区天河路', 1, 3),
('褚十四', 7, 1, '广州市越秀区北京路', 1, 3),
('卫十五', 6, 2, '广州市海珠区江南大道', 1, 3),
('蒋十六', 7, 1, '广州市荔湾区上下九路', 1, 3),
('沈十七', 6, 2, '广州市白云区机场路', 1, 3),
-- 二年级一班class_id=4
('韩十八', 7, 1, '深圳市福田区深南中路', 2, 4),
('杨十九', 8, 2, '深圳市南山区科技园', 2, 4),
('朱二十', 7, 1, '深圳市罗湖区东门步行街', 2, 4),
('秦二一', 8, 2, '深圳市盐田区沙头角', 2, 4),
('尤二二', 7, 1, '深圳市宝安区宝安大道', 2, 4),
-- 二年级二班class_id=5
('许二三', 7, 2, '杭州市西湖区西湖大道', 2, 5),
('何二四', 8, 1, '杭州市上城区解放路', 2, 5),
('吕二五', 7, 2, '杭州市拱墅区武林路', 2, 5),
('施二六', 8, 1, '杭州市滨江区江南大道', 2, 5),
('张二七', 7, 2, '杭州市萧山区市心路', 2, 5),
-- 二年级三班class_id=6
('孔二八', 7, 1, '成都市锦江区春熙路', 2, 6),
('曹二九', 8, 2, '成都市青羊区宽窄巷子', 2, 6),
('严三十', 7, 1, '成都市金牛区天府大道', 2, 6),
('华三一', 8, 2, '成都市武侯区武侯祠大街', 2, 6),
('金三二', 7, 1, '成都市成华区建设路', 2, 6),
-- 三年级一班class_id=7
('魏三三', 8, 2, '武汉市武昌区东湖路', 3, 7),
('陶三四', 9, 1, '武汉市汉口区中山大道', 3, 7),
('姜三五', 8, 2, '武汉市汉阳古琴台路', 3, 7),
('戚三六', 9, 1, '武汉市洪山区光谷广场', 3, 7),
('谢三七', 8, 2, '武汉市青山区和平大道', 3, 7),
-- 三年级二班class_id=8
('邹三八', 8, 1, '西安市雁塔区大雁塔路', 3, 8),
('喻三九', 9, 2, '西安市碑林区南大街', 3, 8),
('柏四十', 8, 1, '西安市莲湖区北大街', 3, 8),
('水四一', 9, 2, '西安市未央区未央路', 3, 8),
('窦四二', 8, 1, '西安市新城区解放路', 3, 8),
-- 三年级三班class_id=9
('章四三', 8, 2, '南京市鼓楼区中山路', 3, 9),
('云四四', 9, 1, '南京市玄武区玄武湖路', 3, 9),
('苏四五', 8, 2, '南京市秦淮区夫子庙路', 3, 9),
('潘四六', 9, 1, '南京市建邺区河西大街', 3, 9),
('葛四七', 8, 2, '南京市雨花台区雨花大道', 3, 9);
```
### 3. 项目启动
1. 用 IDEA 打开项目,等待 Maven 依赖加载完成。
2. 修改 `application.properties` 中的数据库连接信息(用户名、密码)。
3. 运行 `StudentManagementApplication.java``main` 方法,启动项目。
4. 启动成功后,访问 `http://localhost:8080/api/class/list?grade=1`,若返回班级列表则表示运行正常。
## 八、后续优化方向
1. **参数校验**:引入 `Spring Validation`,用注解(如 `@Min`、`@NotBlank`)替代手动参数判断。
2. **统一响应格式**:创建 `ApiResult` 统一返回结构(状态码、提示信息、数据),替代 `HashMap`
3. **DTO/VO 分离**:用 DTO数据传输对象接收前端参数VO视图对象封装返回数据隐藏后端实体细节。
4. **全局异常处理**:用 `@RestControllerAdvice` 捕获异常,统一返回错误信息。
5. **接口文档自动生成**:引入 Swagger/knife4j自动生成接口文档方便前后端联调。
6. **权限控制**:引入 Spring Security 或 Shiro实现用户登录、权限校验。
## 九、注意事项
1. 数据库表名、字段名采用下划线命名实体类采用驼峰命名MyBatis-Plus 会自动转换(需开启 `map-underscore-to-camel-case`)。
2. 分页功能依赖 MyBatis-Plus 分页插件,已在 `MyBatisConfig.java` 中配置,若分页无效需检查该配置。
3. 前端请求需配置代理(如 Vite 代理),将 `/api` 前缀转发到后端地址(`http://localhost:8080`),避免跨域问题。
# 学生管理系统 - 后端说明文档
## 一、项目简介
本项目是一个基于 **Spring Boot + MyBatis-Plus** 开发的学生管理系统后端,提供班级管理、学生信息管理等核心功能,支持分页查询、条件筛选、增删改查等操作,前后端分离架构,适配前端 Vue 项目。
## 二、技术栈
| 技术 | 版本 | 说明 |
| ----------------- | ------ | ---------------------------------- |
| Spring Boot | 2.7.x | 快速开发框架 |
| MyBatis-Plus | 3.5.x | MyBatis 增强工具 |
| MySQL | 8.0 | 关系型数据库 |
| Lombok | 1.18.x | 简化 Java 代码getter/setter 等) |
| Spring Validation | 2.7.x | 参数校验(后续优化用) |
## 三、项目结构
```plaintext
src/
├── main/
│ ├── java/
│ │ └── com/example/studentmanagement/
│ │ ├── StudentManagementApplication.java // 启动类
│ │ ├── common/ // 公共类(后续优化用:统一响应、状态码)
│ │ ├── controller/ // 控制器(接口层)
│ │ │ ├── ClassController.java // 班级管理接口
│ │ │ └── StudentController.java // 学生管理接口
│ │ ├── dto/ // 数据传输对象(后续优化用:接收前端参数)
│ │ ├── entity/ // 实体类(对应数据库表)
│ │ │ ├── GradeClass.java // 班级实体
│ │ │ └── Student.java // 学生实体
│ │ ├── mapper/ // Mapper接口MyBatis-Plus
│ │ │ ├── ClassMapper.java
│ │ │ └── StudentMapper.java
│ │ ├── service/ // 服务层
│ │ │ ├── ClassService.java // 班级服务接口
│ │ │ ├── StudentService.java // 学生服务接口
│ │ │ └── impl/ // 服务实现类
│ │ └── config/ // 配置类
│ │ └── MyBatisConfig.java // MyBatis-Plus配置分页插件等
│ └── resources/
│ ├── application.properties // 配置文件(数据库、端口等)
│ └── mapper/ // MyBatis XML映射文件若有
└── test/ // 测试类(可选)
```
## 四、核心功能说明
### 1. 班级管理
- 查询班级列表(按年级筛选、模糊查询班级名称)
- 新增班级
- 编辑班级信息
- 删除班级(校验班级下是否有学生,有则禁止删除)
- 检查班级是否有学生(用于删除前校验)
### 2. 学生管理
- 分页查询学生列表(支持年级、班级、姓名模糊筛选)
- 新增学生
- 编辑学生信息
- 批量删除学生
## 五、接口文档(当前版本)
### 1. 班级管理接口
| 接口地址 | 请求方式 | 参数说明 | 返回结果 |
| ---------------------- | -------- | ------------------------------------------------------ | -------------------------------------- |
| /api/class/list | GET | grade年级必填、name班级名称可选 | 班级列表List<GradeClass> |
| /api/class/add | POST | name班级名称、grade年级、headTeacher班主任 | 新增结果(布尔值) |
| /api/class/edit | PUT | id班级 ID必填、name、grade、headTeacher | 编辑结果(布尔值) |
| /api/class/delete/{id} | DELETE | id班级 ID路径参数 | 删除结果(布尔值) |
| /api/class/check | GET | classId班级 ID必填 | 是否有学生({hasStudent: true/false} |
### 2. 学生管理接口
| 接口地址 | 请求方式 | 参数说明 | 返回结果 |
| ------------------------- | -------- | ------------------------------------------------------------ | -------------------------------------------- |
| /api/student/list | GET | page页码必填、size每页条数必填、grade年级、classId班级 ID、name学生姓名 | 分页学生列表({list: [...], total: 总条数} |
| /api/student/add | POST | name、age、gender、address、grade、classId | 新增结果(布尔值) |
| /api/student/edit | PUT | id学生 ID必填、name、age、gender、address、grade、classId | 编辑结果(布尔值) |
| /api/student/delete/{ids} | DELETE | ids学生 ID 逗号分隔,如 1,2,3 | 删除结果(布尔值) |
## 六、配置说明application.properties
```properties
# 服务器端口默认8080若冲突可修改
server.port=8080
# 数据库连接配置需修改为你的MySQL信息
spring.datasource.url=jdbc:mysql://localhost:3306/student_management?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root # 你的MySQL用户名
spring.datasource.password=123456 # 你的MySQL密码
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# MyBatis-Plus配置
# 实体类别名扫描包对应entity目录
mybatis-plus.type-aliases-package=com.example.studentmanagement.entity
# XML映射文件路径若有
mybatis-plus.mapper-locations=classpath:mapper/*.xml
# 开启驼峰命名转换如数据库字段class_id -> 实体类classId
mybatis-plus.configuration.map-underscore-to-camel-case=true
```
## 七、使用说明
### 1. 环境准备
- JDK 8+
- MySQL 8.0
- Maven 3.6+(或使用 IDEA 自带 Maven
### 2. 数据库初始化
1. 创建数据库 student_management
```sql
CREATE DATABASE IF NOT EXISTS student_management DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
```
2. 执行数据库脚本创建班级表class和学生表student
```sql
-- 班级表
CREATE TABLE `class` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '班级ID',
`name` varchar(50) NOT NULL COMMENT '班级名称',
`grade` int NOT NULL COMMENT '年级',
`head_teacher` varchar(50) DEFAULT NULL COMMENT '班主任',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='班级表';
-- 学生表关联班级表id
CREATE TABLE `student` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '学生ID',
`name` varchar(50) NOT NULL COMMENT '姓名',
`age` int DEFAULT NULL COMMENT '年龄',
`gender` int DEFAULT NULL COMMENT '性别1-男2-女)',
`address` varchar(255) DEFAULT NULL COMMENT '家庭住址',
`grade` int NOT NULL COMMENT '年级',
`class_id` bigint DEFAULT NULL COMMENT '班级ID关联class表id',
PRIMARY KEY (`id`),
KEY `idx_class_id` (`class_id`),
CONSTRAINT `fk_student_class` FOREIGN KEY (`class_id`) REFERENCES `class` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='学生表';
```
3. 执行测试数据脚本(批量插入班级和学生数据)
```sql
-- 切换到你的数据库(请确保已创建 student_management 数据库)
USE student_management;
-- 清空现有数据(可选,首次执行可跳过,避免重复插入)
-- TRUNCATE TABLE student;
-- TRUNCATE TABLE class;
-- 1. 插入班级数据3个年级每个年级3个班共9个班级
INSERT INTO class (name, grade, head_teacher) VALUES
-- 一年级班级
('一年级一班', 1, '王老师'),
('一年级二班', 1, '李老师'),
('一年级三班', 1, '张老师'),
-- 二年级班级
('二年级一班', 2, '赵老师'),
('二年级二班', 2, '孙老师'),
('二年级三班', 2, '周老师'),
-- 三年级班级
('三年级一班', 3, '吴老师'),
('三年级二班', 3, '郑老师'),
('三年级三班', 3, '钱老师');
-- 2. 插入学生数据每个班级5名学生共45名学生关联正确的 class_id
INSERT INTO student (name, age, gender, address, grade, class_id) VALUES
-- 一年级一班class_id=1
('张三', 6, 1, '北京市海淀区中关村大街', 1, 1),
('李四', 7, 1, '北京市朝阳区建国路', 1, 1),
('王五', 6, 2, '北京市西城区金融街', 1, 1),
('赵六', 7, 2, '北京市东城区王府井大街', 1, 1),
('孙七', 6, 1, '北京市丰台区马家堡东路', 1, 1),
-- 一年级二班class_id=2
('周八', 6, 1, '上海市黄浦区南京东路', 1, 2),
('吴九', 7, 2, '上海市徐汇区淮海中路', 1, 2),
('郑十', 6, 1, '上海市静安区静安寺路', 1, 2),
('钱十一', 7, 2, '上海市长宁区虹桥路', 1, 2),
('冯十二', 6, 1, '上海市普陀区中山北路', 1, 2),
-- 一年级三班class_id=3
('陈十三', 6, 2, '广州市天河区天河路', 1, 3),
('褚十四', 7, 1, '广州市越秀区北京路', 1, 3),
('卫十五', 6, 2, '广州市海珠区江南大道', 1, 3),
('蒋十六', 7, 1, '广州市荔湾区上下九路', 1, 3),
('沈十七', 6, 2, '广州市白云区机场路', 1, 3),
-- 二年级一班class_id=4
('韩十八', 7, 1, '深圳市福田区深南中路', 2, 4),
('杨十九', 8, 2, '深圳市南山区科技园', 2, 4),
('朱二十', 7, 1, '深圳市罗湖区东门步行街', 2, 4),
('秦二一', 8, 2, '深圳市盐田区沙头角', 2, 4),
('尤二二', 7, 1, '深圳市宝安区宝安大道', 2, 4),
-- 二年级二班class_id=5
('许二三', 7, 2, '杭州市西湖区西湖大道', 2, 5),
('何二四', 8, 1, '杭州市上城区解放路', 2, 5),
('吕二五', 7, 2, '杭州市拱墅区武林路', 2, 5),
('施二六', 8, 1, '杭州市滨江区江南大道', 2, 5),
('张二七', 7, 2, '杭州市萧山区市心路', 2, 5),
-- 二年级三班class_id=6
('孔二八', 7, 1, '成都市锦江区春熙路', 2, 6),
('曹二九', 8, 2, '成都市青羊区宽窄巷子', 2, 6),
('严三十', 7, 1, '成都市金牛区天府大道', 2, 6),
('华三一', 8, 2, '成都市武侯区武侯祠大街', 2, 6),
('金三二', 7, 1, '成都市成华区建设路', 2, 6),
-- 三年级一班class_id=7
('魏三三', 8, 2, '武汉市武昌区东湖路', 3, 7),
('陶三四', 9, 1, '武汉市汉口区中山大道', 3, 7),
('姜三五', 8, 2, '武汉市汉阳古琴台路', 3, 7),
('戚三六', 9, 1, '武汉市洪山区光谷广场', 3, 7),
('谢三七', 8, 2, '武汉市青山区和平大道', 3, 7),
-- 三年级二班class_id=8
('邹三八', 8, 1, '西安市雁塔区大雁塔路', 3, 8),
('喻三九', 9, 2, '西安市碑林区南大街', 3, 8),
('柏四十', 8, 1, '西安市莲湖区北大街', 3, 8),
('水四一', 9, 2, '西安市未央区未央路', 3, 8),
('窦四二', 8, 1, '西安市新城区解放路', 3, 8),
-- 三年级三班class_id=9
('章四三', 8, 2, '南京市鼓楼区中山路', 3, 9),
('云四四', 9, 1, '南京市玄武区玄武湖路', 3, 9),
('苏四五', 8, 2, '南京市秦淮区夫子庙路', 3, 9),
('潘四六', 9, 1, '南京市建邺区河西大街', 3, 9),
('葛四七', 8, 2, '南京市雨花台区雨花大道', 3, 9);
```
### 3. 项目启动
1. 用 IDEA 打开项目,等待 Maven 依赖加载完成。
2. 修改 `application.properties` 中的数据库连接信息(用户名、密码)。
3. 运行 `StudentManagementApplication.java``main` 方法,启动项目。
4. 启动成功后,访问 `http://localhost:8080/api/class/list?grade=1`,若返回班级列表则表示运行正常。
## 八、后续优化方向
1. **参数校验**:引入 `Spring Validation`,用注解(如 `@Min`、`@NotBlank`)替代手动参数判断。
2. **统一响应格式**:创建 `ApiResult` 统一返回结构(状态码、提示信息、数据),替代 `HashMap`
3. **DTO/VO 分离**:用 DTO数据传输对象接收前端参数VO视图对象封装返回数据隐藏后端实体细节。
4. **全局异常处理**:用 `@RestControllerAdvice` 捕获异常,统一返回错误信息。
5. **接口文档自动生成**:引入 Swagger/knife4j自动生成接口文档方便前后端联调。
6. **权限控制**:引入 Spring Security 或 Shiro实现用户登录、权限校验。
## 九、注意事项
1. 数据库表名、字段名采用下划线命名实体类采用驼峰命名MyBatis-Plus 会自动转换(需开启 `map-underscore-to-camel-case`)。
2. 分页功能依赖 MyBatis-Plus 分页插件,已在 `MyBatisConfig.java` 中配置,若分页无效需检查该配置。
3. 前端请求需配置代理(如 Vite 代理),将 `/api` 前缀转发到后端地址(`http://localhost:8080`),避免跨域问题。
4. 删除班级时会校验班级下是否有学生,若有则返回删除失败,需先删除该班级下的学生或转移学生到其他班级。

@ -58,7 +58,31 @@
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version> <!-- 版本可根据需求选择,建议使用最新稳定版 -->
<version>3.5.5</version>
</dependency>
<!-- 引入JWT依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot3-starter</artifactId>
<version>1.37.0</version>
</dependency>
</dependencies>

@ -0,0 +1,51 @@
package cn.zyp.stusystem.common;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.naming.AuthenticationException;
import java.nio.file.AccessDeniedException;
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理SaToken未登录异常
@ExceptionHandler(NotLoginException.class)
public Result<Void> handleNotLoginException(NotLoginException e) {
return Result.error(401, "未登录或登录已过期");
}
// 处理SaToken权限不足异常
@ExceptionHandler(NotPermissionException.class)
public Result<Void> handleNotPermissionException(NotPermissionException e) {
return Result.error(403, "无权限访问");
}
// 处理SaToken角色不足异常
@ExceptionHandler(NotRoleException.class)
public Result<Void> handleNotRoleException(NotRoleException e) {
return Result.error(403, "无角色权限");
}
// 处理其他认证异常
@ExceptionHandler(AuthenticationException.class)
public Result<Void> handleException(AuthenticationException e){
return Result.error(401, e.getMessage());
}
// 处理其他权限不足异常
@ExceptionHandler(AccessDeniedException.class)
public Result<Void> handleAccessDeniedException(AccessDeniedException e) {
return Result.error(403, e.getMessage());
}
// 处理其他未知异常
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
e.printStackTrace();
return Result.error(500, "服务器内部错误");
}
}

@ -0,0 +1,63 @@
package cn.zyp.stusystem.common;
import lombok.Data;
@Data
public class Result<T> {
//响应码
private int code;
//响应消息
private String msg;
//响应数据
private T data;
private Result(){}
/**
*
* @param data
* @param <T>
* @return Result
*/
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMsg("success");
result.setData(data);
return result;
}
/**
*
* @param <T>
* @return Result
*/
public static <T> Result<T> success() {
return success(null);
}
/**
*
* @param code
* @param msg
* @param <T>
* @return Result
*/
public static <T> Result<T> error(int code, String msg) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
result.setData(null);
return result;
}
/**
* 500
* @param msg
* @param <T>
* @return Result
*/
public static <T> Result<T> error(String msg) {
return error(500, msg);
}
}

@ -0,0 +1,23 @@
package cn.zyp.stusystem.common;
public enum ResultCode {
SUCCESS(200,"成功"),
ERROR(500,"错误"),
PARAM_ERROR(400,"参数错误");
private final int code;
private final String message;
ResultCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}

@ -0,0 +1,24 @@
package cn.zyp.stusystem.config;
import cn.dev33.satoken.filter.SaServletFilter;
import cn.dev33.satoken.util.SaResult;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SaTokenConfig {
@Bean
public SaServletFilter getSaServletFilter() {
return new SaServletFilter()
.addInclude("/**")
.addExclude("/favicon.ico")
.setAuth(obj -> {
// 这里不需要写权限校验,让注解来处理
// SaRouter.match("/**").notMatch("/api/login").check(StpUtil::checkLogin);
})
.setError(e -> {
return SaResult.error(e.getMessage());
});
}
}

@ -0,0 +1,24 @@
package cn.zyp.stusystem.config;
import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class StpInterfaceImpl implements StpInterface {
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
// 从Session获取权限
return (List<String>) StpUtil.getSessionByLoginId(loginId).get("permissions");
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
String role = (String) StpUtil.getSessionByLoginId(loginId).get("role");
return Arrays.asList(role);
}
}

@ -0,0 +1,42 @@
package cn.zyp.stusystem.constant;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// constant/PermissionConstants.java
public class PermissionConstants {
public static final String ROLE_ADMIN = "admin";
public static final String ROLE_HEAD_TEACHER = "head_teacher";
public static final String ROLE_TEACHER = "teacher";
public static final String CLASS_VIEW = "class:view";
public static final String CLASS_ADD = "class:add";
public static final String CLASS_EDIT = "class:edit";
public static final String CLASS_DELETE = "class:delete";
public static final String STUDENT_VIEW = "student:view";
public static final String STUDENT_ADD = "student:add";
public static final String STUDENT_EDIT = "student:edit";
public static final String STUDENT_DELETE = "student:delete";
public static final Map<String, List<String>> ROLE_PERMISSIONS = new HashMap<>();
static {
ROLE_PERMISSIONS.put(ROLE_ADMIN, Arrays.asList(
CLASS_VIEW, CLASS_ADD, CLASS_EDIT, CLASS_DELETE,
STUDENT_VIEW, STUDENT_ADD, STUDENT_EDIT, STUDENT_DELETE
));
ROLE_PERMISSIONS.put(ROLE_HEAD_TEACHER, Arrays.asList(
CLASS_VIEW,
STUDENT_VIEW, STUDENT_ADD, STUDENT_EDIT, STUDENT_DELETE
));
ROLE_PERMISSIONS.put(ROLE_TEACHER, Arrays.asList(
CLASS_VIEW,
STUDENT_VIEW
));
}
}

@ -1,11 +1,10 @@
package cn.zyp.stusystem.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.zyp.stusystem.entity.GradeClass;
import cn.zyp.stusystem.service.ClassService;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
@ -16,53 +15,61 @@ import java.util.Map;
@RequestMapping("/api/class")
public class ClassController {
//测试git
@Autowired
private ClassService classService;
//根据年级查询班级列表
// 根据年级查询班级列表 - 所有登录用户都可以查看
@SaCheckLogin
@SaCheckPermission("class:view")
@GetMapping("/list")
public Map<String, Object> getClassList(@RequestParam Integer grade,@RequestParam(required = false) String name){
public Map<String, Object> getClassList(@RequestParam Integer grade, @RequestParam(required = false) String name) {
List<GradeClass> gradeClasses = classService.findClassByGradeId(grade);
System.out.println(gradeClasses);
//过滤班级名称
if(name != null){
gradeClasses= gradeClasses.stream().filter(gradeClass -> gradeClass.getName().contains(name)).toList();
// 过滤班级名称
if (name != null) {
gradeClasses = gradeClasses.stream().filter(gradeClass -> gradeClass.getName().contains(name)).toList();
}
Map<String,Object> result = new HashMap<>();
result.put("data",gradeClasses);
Map<String, Object> result = new HashMap<>();
result.put("data", gradeClasses);
return result;
}
//新增班级
// 新增班级 - 只有管理员有权限
@SaCheckLogin
@SaCheckPermission("class:add")
@PostMapping("/add")
public boolean addClass(@RequestBody GradeClass gradeClass){
public boolean addClass(@RequestBody GradeClass gradeClass) {
return classService.save(gradeClass);
}
//编辑班级
// 编辑班级 - 只有管理员有权限
@SaCheckLogin
@SaCheckPermission("class:edit")
@PutMapping("/edit")
public boolean editClass(@RequestBody GradeClass gradeClass){
public boolean editClass(@RequestBody GradeClass gradeClass) {
return classService.updateById(gradeClass);
}
//删除班级
// 删除班级 - 只有管理员有权限
@SaCheckLogin
@SaCheckPermission("class:delete")
@DeleteMapping("/delete/{id}")
public boolean deleteClass(@PathVariable Integer id){
if(classService.isClassHasStudent(id)){
public boolean deleteClass(@PathVariable Integer id) {
if (classService.isClassHasStudent(id)) {
return false;
}else {
} else {
return classService.removeById(id);
}
}
// 检查班级是否有学生 - 所有登录用户都可以查看
@SaCheckLogin
@SaCheckPermission("class:view")
@GetMapping("/check")
//检查班级是否有学生
public Map<String,Object> checkClassHasStudent(@RequestParam Integer classId){
Map<String,Object> result = new HashMap<>();
result.put("data",classService.isClassHasStudent(classId));
public Map<String, Object> checkClassHasStudent(@RequestParam Integer classId) {
Map<String, Object> result = new HashMap<>();
result.put("data", classService.isClassHasStudent(classId));
return result;
}
}
}

@ -0,0 +1,73 @@
package cn.zyp.stusystem.controller;
import cn.dev33.satoken.stp.StpUtil;
import cn.zyp.stusystem.common.Result;
import cn.zyp.stusystem.dto.LoginDTO;
import cn.zyp.stusystem.entity.User;
import cn.zyp.stusystem.service.RoleService;
import cn.zyp.stusystem.service.UserService;
import cn.zyp.stusystem.vo.LoginResult;
import cn.zyp.stusystem.vo.UserVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/api")
public class LoginController {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
@PostMapping("/login")
public Result<LoginResult> login(@RequestBody LoginDTO loginDTO) {
// 1. 根据用户名查询用户信息
User user = userService.getByUsername(loginDTO.getUsername());
// 2. 校验用户是否存在以及密码是否正确
if(user == null || !user.getPassword().equals(loginDTO.getPassword())) {
return Result.error(401, "用户名或密码错误");
}
// 3. SaToken登录自动生成token
StpUtil.login(user.getId());
// 4. 获取用户权限(从数据库获取,而不是从常量)
List<String> permissions = userService.getUserPermissions(user.getId());
// 5. 获取角色ID如果user中没有roleId则根据role查找
Long roleId = user.getRoleId();
if (roleId == null && user.getRole() != null) {
roleId = roleService.getRoleIdByRoleCode(user.getRole());
}
// 6. 在Session中存储角色和权限
StpUtil.getSession().set("role", user.getRole());
StpUtil.getSession().set("permissions", permissions);
if (roleId != null) {
StpUtil.getSession().set("roleId", roleId);
}
// 7. 构建返回数据
UserVO userVO = new UserVO();
userVO.setId(user.getId());
userVO.setUsername(user.getUsername());
userVO.setRole(user.getRole());
userVO.setRoleId(roleId != null ? roleId.toString() : null); // 转换为字符串,前端需要
userVO.setName(user.getName());
userVO.setPermissions(permissions);
LoginResult loginResult = new LoginResult();
loginResult.setToken(StpUtil.getTokenValue());
loginResult.setUserInfo(userVO);
return Result.success(loginResult);
}
}

@ -0,0 +1,38 @@
package cn.zyp.stusystem.controller;
import cn.zyp.stusystem.common.Result;
import cn.zyp.stusystem.entity.Permission;
import cn.zyp.stusystem.service.PermissionService;
import cn.zyp.stusystem.vo.PermissionVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/permission")
public class PermissionController {
@Autowired
private PermissionService permissionService;
/**
*
*/
@GetMapping("/all")
public Result<List<PermissionVO>> getAllPermissions() {
List<Permission> permissions = permissionService.getAllEnabledPermissions();
List<PermissionVO> voList = permissions.stream()
.map(permission -> {
PermissionVO vo = new PermissionVO();
BeanUtils.copyProperties(permission, vo);
return vo;
})
.collect(Collectors.toList());
return Result.success(voList);
}
}

@ -0,0 +1,133 @@
package cn.zyp.stusystem.controller;
import cn.zyp.stusystem.common.Result;
import cn.zyp.stusystem.dto.SaveRolePermissionsDTO;
import cn.zyp.stusystem.entity.Role;
import cn.zyp.stusystem.service.RolePermissionService;
import cn.zyp.stusystem.service.RoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/role")
public class RoleController {
@Autowired
private RoleService roleService;
@Autowired
private RolePermissionService rolePermissionService;
/**
*
*/
@GetMapping
public Result<Map<String, Object>> getRoleList(
@RequestParam(defaultValue = "1") Integer current,
@RequestParam(defaultValue = "10") Integer size,
@RequestParam(required = false) String roleName) {
Page<Role> page = new Page<>(current, size);
LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
if (roleName != null && !roleName.trim().isEmpty()) {
wrapper.like(Role::getRoleName, roleName);
}
wrapper.orderByDesc(Role::getCreatedAt);
Page<Role> rolePage = roleService.page(page, wrapper);
Map<String, Object> result = new HashMap<>();
result.put("list", rolePage.getRecords());
result.put("total", rolePage.getTotal());
return Result.success(result);
}
/**
*
*/
@GetMapping("/{roleId}")
public Result<Role> getRoleById(@PathVariable Long roleId) {
Role role = roleService.getById(roleId);
if (role == null) {
return Result.error(404, "角色不存在");
}
return Result.success(role);
}
/**
*
*/
@PostMapping
public Result<Role> createRole(@RequestBody Role role) {
if (role.getStatus() == null) {
role.setStatus(1);
}
boolean success = roleService.save(role);
if (success) {
// 返回创建的角色对象包含ID这样前端才能获取到角色ID来保存权限
return Result.success(role);
} else {
return Result.error("创建失败");
}
}
/**
*
*/
@PutMapping("/{roleId}")
public Result<Boolean> updateRole(@PathVariable Long roleId, @RequestBody Role role) {
role.setId(roleId);
boolean success = roleService.updateById(role);
return success ? Result.success(true) : Result.error("更新失败");
}
/**
*
*/
@DeleteMapping("/{roleId}")
public Result<Boolean> deleteRole(@PathVariable Long roleId) {
Role role = roleService.getById(roleId);
if (role == null) {
return Result.error(404, "角色不存在");
}
boolean success = roleService.removeById(roleId);
return success ? Result.success(true) : Result.error("删除失败");
}
/**
*
*/
@GetMapping("/{roleId}/permissions")
public Result<List<String>> getRolePermissions(@PathVariable Long roleId) {
List<String> permissionCodes = rolePermissionService.getPermissionCodesByRoleId(roleId);
return Result.success(permissionCodes);
}
/**
*
*/
@PostMapping("/{roleId}/permissions")
public Result<Boolean> saveRolePermissions(
@PathVariable Long roleId,
@RequestBody SaveRolePermissionsDTO dto) {
System.out.println("收到权限保存请求: roleId=" + roleId + ", permissions=" + dto.getPermissions());
try {
rolePermissionService.saveRolePermissions(roleId, dto.getPermissions());
System.out.println("权限保存成功");
return Result.success(true);
} catch (Exception e) {
System.out.println("权限保存失败: " + e.getMessage());
e.printStackTrace();
return Result.error("保存失败: " + e.getMessage());
}
}
}

@ -0,0 +1,19 @@
package cn.zyp.stusystem.controller;
import cn.zyp.stusystem.common.Result;
import cn.zyp.stusystem.dto.SaveRolePermissionsDTO;
import cn.zyp.stusystem.service.RolePermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/role")
public class RolePermissionController {
@Autowired
private RolePermissionService rolePermissionService;
}

@ -1,6 +1,7 @@
package cn.zyp.stusystem.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.zyp.stusystem.entity.Student;
import cn.zyp.stusystem.service.StudentService;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -18,46 +19,59 @@ public class StudentController {
@Autowired
private StudentService studentService;
//获取学生列表
// 获取学生列表 - 所有登录用户都可以查看
@SaCheckLogin
@SaCheckPermission("student:view")
@GetMapping("/list")
public Map<String,Object> getStudentList(
public Map<String, Object> getStudentList(
@RequestParam Integer page, // 页码从1开始
@RequestParam Integer size, // 每页条数
@RequestParam Integer grade, // 年级1-3
@RequestParam(required = false) Long classId, // 班级ID
@RequestParam(required = false) String name
){
IPage<Student> studentPage = studentService.selectPageStudent(page,size,grade,classId,name);
Map<String,Object> result = new HashMap<>();
result.put("list",studentPage.getRecords());
result.put("total",studentPage.getTotal());
) {
// 参数校验
if (page == null || page < 1) {
page = 1;
}
IPage<Student> studentPage = studentService.selectPageStudent(page, size, grade, classId, name);
Map<String, Object> result = new HashMap<>();
result.put("list", studentPage.getRecords());
result.put("total", studentPage.getTotal());
//测试
// 测试
System.out.println("后端返回的学生数据:" + result);
return result;
}
//新增学生
// 新增学生 - 管理员和班主任有权限
@SaCheckLogin
@SaCheckPermission("student:add")
@PostMapping("/add")
public boolean addStudent(@RequestBody Student student){
public boolean addStudent(@RequestBody Student student) {
return studentService.save(student);
}
//编辑学生
// 编辑学生 - 管理员和班主任有权限
@SaCheckLogin
@SaCheckPermission("student:edit")
@PutMapping("/edit")
public boolean editStudent(@RequestBody Student student){
public boolean editStudent(@RequestBody Student student) {
return studentService.updateById(student);
}
//删除学生
// 删除学生 - 管理员和班主任有权限
@SaCheckLogin
@SaCheckPermission("student:delete")
@DeleteMapping("/delete/{id}")
public boolean deleteStudent(@PathVariable String id){
if(id.contains(",")){
public boolean deleteStudent(@PathVariable String id) {
if (id.contains(",")) {
String[] ids = id.split(",");
return studentService.removeByIds(Arrays.asList(ids));
}else {
} else {
return studentService.removeById(id);
}
}
}
}

@ -0,0 +1,52 @@
package cn.zyp.stusystem.controller;
import cn.dev33.satoken.annotation.SaCheckLogin;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.stp.StpUtil;
import cn.zyp.stusystem.common.Result;
import cn.zyp.stusystem.dto.UserSimpleDTO;
import cn.zyp.stusystem.entity.User;
import cn.zyp.stusystem.service.UserService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
@SaCheckLogin
@SaCheckPermission("class:edit") // 或者更宽松的权限
@GetMapping("/teachers")
public Result<List<UserSimpleDTO>> listTeachers() {
List<UserSimpleDTO> teachers = userService.list(
new LambdaQueryWrapper<User>()
.in(User::getRole, Arrays.asList("head_teacher", "teacher"))
.eq(User::getStatus, 1)
.select(User::getId, User::getName)
).stream()
.map(u -> new UserSimpleDTO(u.getId(), u.getName()))
.collect(Collectors.toList());
return Result.success(teachers);
}
/**
*
*/
@GetMapping("/permissions")
public Result<List<String>> getUserPermissions() {
// 从Token中获取当前用户ID
Long userId = StpUtil.getLoginIdAsLong();
List<String> permissions = userService.getUserPermissions(userId);
return Result.success(permissions);
}
}

@ -0,0 +1,9 @@
package cn.zyp.stusystem.dto;
import lombok.Data;
@Data
public class LoginDTO {
private String username;
private String password;
}

@ -0,0 +1,9 @@
package cn.zyp.stusystem.dto;
import lombok.Data;
import java.util.List;
@Data
public class SaveRolePermissionsDTO {
private List<String> permissions; // 权限码列表
}

@ -0,0 +1,13 @@
package cn.zyp.stusystem.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserSimpleDTO {
private Long id;
private String name;
}

@ -22,6 +22,11 @@ public class GradeClass {
@TableField("grade")
private Integer grade;
@TableField("head_teacher")
// 数据库字段是 head_teacher_id类型应该是Long关联user表的id
@TableField("head_teacher_id")
private Long headTeacherId;
@TableField(exist = false)
private String headTeacher;
}
}

@ -0,0 +1,25 @@
package cn.zyp.stusystem.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("permissions")
public class Permission {
@TableId(type = IdType.AUTO)
private Long id;
private String code; // 权限码,如 student:add
private String name; // 权限名称,如 学生新增
private String module; // 所属模块,如 student
private String moduleName; // 模块名称,如 学生管理
private String description; // 权限描述
private Integer sortOrder; // 排序顺序
private Integer status; // 状态1-启用0-禁用
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

@ -0,0 +1,28 @@
package cn.zyp.stusystem.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("roles")
public class Role {
@TableId(type = IdType.AUTO)
private Long id;
private String roleCode;
private String roleName;
private Integer status;
private String remark;
// 前端期望字段名是 createTime
@JsonProperty("createTime")
private LocalDateTime createdAt;
@JsonProperty("updateTime")
private LocalDateTime updatedAt;
}

@ -0,0 +1,19 @@
package cn.zyp.stusystem.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("role_permissions")
public class RolePermission {
@TableId(type = IdType.AUTO)
private Long id;
private Long roleId; // 角色ID
private String permissionCode; // 权限码
private LocalDateTime createdAt;
}

@ -0,0 +1,23 @@
package cn.zyp.stusystem.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.util.Date;
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
private String role; // admin, head_teacher, teacher保留用于兼容
private Long roleId; // 角色ID新增字段
private String name;
private Integer status;
private Date createTime;
}

@ -0,0 +1,9 @@
package cn.zyp.stusystem.mapper;
import cn.zyp.stusystem.entity.Permission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface PermissionMapper extends BaseMapper<Permission> {
}

@ -0,0 +1,10 @@
package cn.zyp.stusystem.mapper;
import cn.zyp.stusystem.entity.Role;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RoleMapper extends BaseMapper<Role> {
// 不需要自定义方法直接在Service中使用LambdaQueryWrapper
}

@ -0,0 +1,10 @@
package cn.zyp.stusystem.mapper;
import cn.zyp.stusystem.entity.RolePermission;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface RolePermissionMapper extends BaseMapper<RolePermission> {
// 不需要自定义方法直接在Service中使用LambdaQueryWrapper
}

@ -0,0 +1,9 @@
package cn.zyp.stusystem.mapper;
import cn.zyp.stusystem.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

@ -0,0 +1,13 @@
package cn.zyp.stusystem.service;
import cn.zyp.stusystem.entity.Permission;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface PermissionService extends IService<Permission> {
/**
*
*/
List<Permission> getAllEnabledPermissions();
}

@ -0,0 +1,23 @@
package cn.zyp.stusystem.service;
import com.baomidou.mybatisplus.extension.service.IService;
import cn.zyp.stusystem.entity.RolePermission;
import java.util.List;
public interface RolePermissionService extends IService<RolePermission> {
/**
* ID
*/
List<String> getPermissionCodesByRoleId(Long roleId);
/**
* ID
*/
List<String> getPermissionCodesByRoleIds(List<Long> roleIds);
/**
*
*/
void saveRolePermissions(Long roleId, List<String> permissionCodes);
}

@ -0,0 +1,11 @@
package cn.zyp.stusystem.service;
import cn.zyp.stusystem.entity.Role;
import com.baomidou.mybatisplus.extension.service.IService;
public interface RoleService extends IService<Role> {
/**
* ID
*/
Long getRoleIdByRoleCode(String roleCode);
}

@ -0,0 +1,18 @@
package cn.zyp.stusystem.service;
import cn.zyp.stusystem.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface UserService extends IService<User> {
/**
*
*/
User getByUsername(String username);
/**
* ID
*/
List<String> getUserPermissions(Long userId);
}

@ -3,17 +3,13 @@ package cn.zyp.stusystem.service.impl;
import cn.zyp.stusystem.entity.GradeClass;
import cn.zyp.stusystem.entity.Student;
import cn.zyp.stusystem.entity.User;
import cn.zyp.stusystem.mapper.ClassMapper;
import cn.zyp.stusystem.mapper.StudentMapper;
import cn.zyp.stusystem.mapper.UserMapper;
import cn.zyp.stusystem.service.ClassService;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.ConfigurationKeys;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -25,12 +21,27 @@ public class ClassServiceImpl extends ServiceImpl<ClassMapper, GradeClass> imple
@Autowired
private StudentMapper studentMapper;
@Autowired
private UserMapper userMapper;
@Override
public List<GradeClass> findClassByGradeId(Integer gradeId) {
QueryWrapper<GradeClass> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("grade",gradeId);
return baseMapper.selectList(queryWrapper);
List<GradeClass> classes = baseMapper.selectList(queryWrapper);
// 为每个班级设置班主任姓名
for (GradeClass gradeClass : classes) {
if (gradeClass.getHeadTeacherId() != null) {
// 根据headTeacherId查询班主任姓名
User teacher = userMapper.selectById(gradeClass.getHeadTeacherId());
if (teacher != null) {
gradeClass.setHeadTeacher(teacher.getName());
}
}
}
return classes;
}
@Override

@ -0,0 +1,23 @@
package cn.zyp.stusystem.service.impl;
import cn.zyp.stusystem.entity.Permission;
import cn.zyp.stusystem.mapper.PermissionMapper;
import cn.zyp.stusystem.service.PermissionService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission>
implements PermissionService {
@Override
public List<Permission> getAllEnabledPermissions() {
return list(new LambdaQueryWrapper<Permission>()
.eq(Permission::getStatus, 1)
.orderByAsc(Permission::getModule, Permission::getSortOrder)
);
}
}

@ -0,0 +1,79 @@
package cn.zyp.stusystem.service.impl;
import cn.zyp.stusystem.entity.RolePermission;
import cn.zyp.stusystem.mapper.RolePermissionMapper;
import cn.zyp.stusystem.service.RolePermissionService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class RolePermissionServiceImpl extends ServiceImpl<RolePermissionMapper, RolePermission>
implements RolePermissionService {
@Autowired
private RolePermissionMapper rolePermissionMapper;
@Override
public List<String> getPermissionCodesByRoleId(Long roleId) {
if (roleId == null) {
return List.of();
}
// 使用MyBatis-Plus的LambdaQueryWrapper查询
List<RolePermission> list = list(new LambdaQueryWrapper<RolePermission>()
.eq(RolePermission::getRoleId, roleId)
.select(RolePermission::getPermissionCode) // 只查询permission_code字段
);
// 提取permissionCode
return list.stream()
.map(RolePermission::getPermissionCode)
.collect(Collectors.toList());
}
@Override
public List<String> getPermissionCodesByRoleIds(List<Long> roleIds) {
if (roleIds == null || roleIds.isEmpty()) {
return List.of();
}
// 使用MyBatis-Plus的in查询
List<RolePermission> list = list(new LambdaQueryWrapper<RolePermission>()
.in(RolePermission::getRoleId, roleIds)
.select(RolePermission::getPermissionCode) // 只查询permission_code字段
);
// 提取permissionCode并去重
return list.stream()
.map(RolePermission::getPermissionCode)
.distinct() // 去重
.collect(Collectors.toList());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void saveRolePermissions(Long roleId, List<String> permissionCodes) {
// 1. 删除该角色的所有权限
LambdaQueryWrapper<RolePermission> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(RolePermission::getRoleId, roleId);
rolePermissionMapper.delete(wrapper);
// 2. 批量插入新权限
if (permissionCodes != null && !permissionCodes.isEmpty()) {
List<RolePermission> rolePermissions = permissionCodes.stream()
.map(code -> {
RolePermission rp = new RolePermission();
rp.setRoleId(roleId);
rp.setPermissionCode(code);
rp.setCreatedAt(LocalDateTime.now());
return rp;
})
.collect(Collectors.toList());
this.saveBatch(rolePermissions);
}
}
}

@ -0,0 +1,21 @@
package cn.zyp.stusystem.service.impl;
import cn.zyp.stusystem.entity.Role;
import cn.zyp.stusystem.mapper.RoleMapper;
import cn.zyp.stusystem.service.RoleService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
@Override
public Long getRoleIdByRoleCode(String roleCode) {
Role role = getOne(new LambdaQueryWrapper<Role>()
.eq(Role::getRoleCode, roleCode)
.eq(Role::getStatus, 1)
);
return role != null ? role.getId() : null;
}
}

@ -0,0 +1,55 @@
package cn.zyp.stusystem.service.impl;
import cn.zyp.stusystem.entity.User;
import cn.zyp.stusystem.mapper.UserMapper;
import cn.zyp.stusystem.service.RolePermissionService;
import cn.zyp.stusystem.service.RoleService;
import cn.zyp.stusystem.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private RolePermissionService rolePermissionService;
@Autowired
private RoleService roleService;
@Override
public User getByUsername(String username) {
return lambdaQuery().eq(User::getUsername, username).one();
}
@Override
public List<String> getUserPermissions(Long userId) {
// 1. 查询用户信息
User user = getById(userId);
if (user == null) {
return List.of();
}
// 2. 获取角色ID优先使用roleId如果没有则根据role查找
Long roleId = user.getRoleId();
if (roleId == null && user.getRole() != null) {
roleId = roleService.getRoleIdByRoleCode(user.getRole());
// 如果找到了roleId更新到user表优化后续查询
if (roleId != null) {
user.setRoleId(roleId);
updateById(user);
}
}
// 3. 如果还是没有roleId返回空列表
if (roleId == null) {
return List.of();
}
// 4. 查询该角色的所有权限
return rolePermissionService.getPermissionCodesByRoleId(roleId);
}
}

@ -0,0 +1,10 @@
package cn.zyp.stusystem.vo;
import cn.zyp.stusystem.vo.UserVO;
import lombok.Data;
@Data
public class LoginResult {
private String token;
private UserVO userInfo;
}

@ -0,0 +1,11 @@
package cn.zyp.stusystem.vo;
import lombok.Data;
@Data
public class PermissionVO {
private String code; // 权限码
private String name; // 权限名称
private String module; // 所属模块
private String moduleName; // 模块名称
}

@ -0,0 +1,14 @@
package cn.zyp.stusystem.vo;
import lombok.Data;
import java.util.List;
@Data
public class UserVO {
private Long id;
private String username;
private String role; // 角色代码,如 admin
private String roleId; // 角色ID字符串类型前端需要
private String name;
private List<String> permissions; // 权限列表
}

@ -5,9 +5,18 @@ spring:
username: root
password: 123456
# MyBatis-Plus ??
mybatis-plus:
mapper-locations: classpath:mapper/*.xml # Mapper.xml ????
type-aliases-package: com.example.student.entity # ??????
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # ??SQL?????????
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # ??SQL?????????
sa-token:
token-name: Authorization # token名称与前端对应
timeout: 7200 # token有效期
active-timeout: -1 # 活跃有效期(-1代表不限制
is-concurrent: true # 是否允许并发登录
is-share: false # 在多人登录时是否共享token
token-style: uuid # token生成风格

@ -1,13 +0,0 @@
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/student_management?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
# MyBatis-Plus ??
mybatis-plus:
mapper-locations: classpath:mapper/*.xml # Mapper.xml ????
type-aliases-package: com.example.student.entity # ??????
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # ??SQL?????????
Loading…
Cancel
Save