stu01:创建项目到学生接口完成
This commit is contained in:
parent
492b497d63
commit
c2c9cc4e50
32
stu01/HELP.md
Normal file
32
stu01/HELP.md
Normal file
@ -0,0 +1,32 @@
|
||||
# Read Me First
|
||||
The following was discovered as part of building this project:
|
||||
|
||||
* The JVM level was changed from '25' to '24', review the [JDK Version Range](https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions#jdk-version-range) on the wiki for more details.
|
||||
|
||||
# Getting Started
|
||||
|
||||
### Reference Documentation
|
||||
For further reference, please consider the following sections:
|
||||
|
||||
* [Official Apache Maven documentation](https://maven.apache.org/guides/index.html)
|
||||
* [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/3.4.12/maven-plugin)
|
||||
* [Create an OCI image](https://docs.spring.io/spring-boot/3.4.12/maven-plugin/build-image.html)
|
||||
* [Spring Web](https://docs.spring.io/spring-boot/3.4.12/reference/web/servlet.html)
|
||||
* [MyBatis Framework](https://mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/)
|
||||
|
||||
### Guides
|
||||
The following guides illustrate how to use some features concretely:
|
||||
|
||||
* [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/)
|
||||
* [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/)
|
||||
* [Building REST services with Spring](https://spring.io/guides/tutorials/rest/)
|
||||
* [MyBatis Quick Start](https://github.com/mybatis/spring-boot-starter/wiki/Quick-Start)
|
||||
* [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/)
|
||||
|
||||
### Maven Parent overrides
|
||||
|
||||
Due to Maven's design, elements are inherited from the parent POM to the project POM.
|
||||
While most of the inheritance is fine, it also inherits unwanted elements like `<license>` and `<developers>` from the parent.
|
||||
To prevent this, the project POM contains empty overrides for these elements.
|
||||
If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.
|
||||
|
||||
89
stu01/db.sql
Normal file
89
stu01/db.sql
Normal file
@ -0,0 +1,89 @@
|
||||
-- 切换到test01_db数据库
|
||||
USE stu01_db;
|
||||
|
||||
-- 1. 用户表(sys_user):存储登录账号、密码、权限类型
|
||||
DROP TABLE IF EXISTS sys_user;
|
||||
CREATE TABLE sys_user (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '用户ID(主键)' PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL COMMENT '登录账号(唯一)',
|
||||
password VARCHAR(50) NOT NULL COMMENT '登录密码(无需加密,测试用)',
|
||||
user_type TINYINT NOT NULL COMMENT '权限类型:1-学生 2-教师 3-管理员',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
real_name VARCHAR(50) NOT NULL COMMENT '真实姓名'
|
||||
) COMMENT '用户表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 添加账号唯一索引
|
||||
CREATE UNIQUE INDEX idx_sys_user_username ON sys_user(username);
|
||||
|
||||
-- 插入测试数据(管理员账号:admin/123456;学生账号:stu001/123456;教师账号:tea001/123456)
|
||||
INSERT INTO sys_user (username, password, user_type) VALUES
|
||||
('admin', '123456', 3),
|
||||
('stu001', '123456', 1),
|
||||
('tea001', '123456', 2);
|
||||
|
||||
-- 2. 学生表(student):存储学生基础信息
|
||||
DROP TABLE IF EXISTS student;
|
||||
CREATE TABLE student (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '学生ID(主键)' PRIMARY KEY,
|
||||
stu_no VARCHAR(8) NOT NULL COMMENT '学号(8位纯数字,唯一)',
|
||||
name VARCHAR(20) NOT NULL COMMENT '学生姓名',
|
||||
major VARCHAR(30) NOT NULL COMMENT '专业(仅允许:计算机/数学/英语)',
|
||||
user_id BIGINT NOT NULL COMMENT '关联用户ID',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
-- 外键关联用户表
|
||||
CONSTRAINT fk_student_user_id FOREIGN KEY (user_id) REFERENCES sys_user(id)
|
||||
) COMMENT '学生表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 添加学号唯一索引
|
||||
CREATE UNIQUE INDEX idx_student_stu_no ON student(stu_no);
|
||||
|
||||
-- 插入测试数据(关联学生账号stu001的user_id,需先查询sys_user中stu001的id再替换,示例中假设id=2)
|
||||
INSERT INTO student (stu_no, name, major, user_id) VALUES ('20250001', '张三', '计算机', 2);
|
||||
|
||||
-- 3. 教师表(teacher):存储教师基础信息
|
||||
DROP TABLE IF EXISTS teacher;
|
||||
CREATE TABLE teacher (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '教师ID(主键)' PRIMARY KEY,
|
||||
tea_no VARCHAR(8) NOT NULL COMMENT '教师编号(8位纯数字,唯一)',
|
||||
name VARCHAR(20) NOT NULL COMMENT '教师姓名',
|
||||
user_id BIGINT NOT NULL COMMENT '关联用户ID',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
-- 外键关联用户表
|
||||
CONSTRAINT fk_teacher_user_id FOREIGN KEY (user_id) REFERENCES sys_user(id)
|
||||
) COMMENT '教师表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 添加教师编号唯一索引
|
||||
CREATE UNIQUE INDEX idx_teacher_tea_no ON teacher(tea_no);
|
||||
|
||||
-- 插入测试数据(关联教师账号tea001的user_id,示例中假设id=3)
|
||||
INSERT INTO teacher (tea_no, name, user_id) VALUES ('20250001', '李老师', 3);
|
||||
|
||||
-- 4. 课程表(course):存储课程信息,关联授课教师
|
||||
DROP TABLE IF EXISTS course;
|
||||
CREATE TABLE course (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '课程ID(主键)' PRIMARY KEY,
|
||||
course_name VARCHAR(50) NOT NULL COMMENT '课程名称(仅允许中文/数字/字母,长度2-20)',
|
||||
tea_id BIGINT NOT NULL COMMENT '关联教师ID',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
-- 外键关联教师表(用于教师删除时的关联拦截)
|
||||
CONSTRAINT fk_course_tea_id FOREIGN KEY (tea_id) REFERENCES teacher(id)
|
||||
) COMMENT '课程表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 插入测试数据(关联李老师的teacher_id,示例中假设id=1)
|
||||
INSERT INTO course (course_name, tea_id) VALUES ('Java编程', 1);
|
||||
|
||||
-- 5. 学分表(score):存储学生课程成绩,关联学生和课程
|
||||
DROP TABLE IF EXISTS score;
|
||||
CREATE TABLE score (
|
||||
id BIGINT AUTO_INCREMENT COMMENT '学分ID(主键)' PRIMARY KEY,
|
||||
stu_id BIGINT NOT NULL COMMENT '关联学生ID',
|
||||
course_id BIGINT NOT NULL COMMENT '关联课程ID',
|
||||
score INT NOT NULL COMMENT '分数(0-100)',
|
||||
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
-- 外键关联学生表和课程表
|
||||
CONSTRAINT fk_score_stu_id FOREIGN KEY (stu_id) REFERENCES student(id),
|
||||
CONSTRAINT fk_score_course_id FOREIGN KEY (course_id) REFERENCES course(id)
|
||||
) COMMENT '学分表' ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
-- 插入测试数据(关联张三的student_id=1,Java编程的course_id=1)
|
||||
INSERT INTO score (stu_id, course_id, score) VALUES (1, 1, 90);
|
||||
147
stu01/pom.xml
Normal file
147
stu01/pom.xml
Normal file
@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.4.12</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>top.awinx</groupId>
|
||||
<artifactId>stu01</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>stu01</name>
|
||||
<description>Demo project for Spring Boot</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>24</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
||||
<version>3.0.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysql</groupId>
|
||||
<artifactId>mysql-connector-j</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mybatis.spring.boot</groupId>
|
||||
<artifactId>mybatis-spring-boot-starter-test</artifactId>
|
||||
<version>3.0.5</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>3.5.14</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>0.13.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>0.13.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
|
||||
<version>0.13.0</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 核心:Jakarta Validation API(接口层) -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<version>3.1.0</version> <!-- 适配Jakarta EE 10,稳定版 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 必需:Hibernate Validator(API的官方实现) -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
<version>8.0.1.Final</version> <!-- 与API 3.1.0兼容 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 可选:注解处理器(提升编译期校验体验) -->
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator-annotation-processor</artifactId>
|
||||
<version>8.0.1.Final</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
41
stu01/prompt.txt
Normal file
41
stu01/prompt.txt
Normal file
@ -0,0 +1,41 @@
|
||||
一、核心协作规则重申
|
||||
|
||||
|
||||
任务拆分:严格按照《学生管理系统(测试用例适配)开发任务计划表》执行,每个会话仅完成 1 个最小粒度的任务(如 1.1、1.2),确保任务足够简单、无遗漏;
|
||||
计划表更新:每完成 1 个任务后,在输出末尾更新计划表,标注对应任务 “完成状态” 为 “已完成”,并在 “备注” 栏记录执行细节、遇到的问题、后续注意点(规避遗忘 / 幻觉);
|
||||
代码要求:所有开发代码仅满足测试用例覆盖需求,尽可能简洁,无冗余逻辑、无过度设计 / 优化(如密码无需加密、并发仅保证接收请求即可);
|
||||
协作模式:由助手主导任务拆解与代码编写,用户负责实际操作(创建项目、粘贴代码、运行调试),助手需提供 “复制即用” 的代码及清晰的操作指引;
|
||||
输出结构:每个任务的输出需包含「操作指引」「核心代码」「验证方式」「更新后的计划表」四部分,确保用户可直接落地。
|
||||
|
||||
|
||||
二、任务计划表引用
|
||||
所有操作均基于下方《学生管理系统(测试用例适配)开发任务计划表》,每次会话需携带该计划表的最新版本(含已完成任务标注):
|
||||
阶段 任务 ID 任务名称 核心目标 关键提示词 完成状态 备注(仅保留执行结果和后续影响)
|
||||
1. 项目初始化与环境搭建 1.1 创建 SpringBoot 后端基础项目 搭建后端工程骨架,引入核心依赖 SpringBoot、MySQL 驱动、MyBatis、JWT、lombok 已完成 Spring Boot 3.4.12,包名 top.awinx.stu01,Java 25;依赖:MyBatis-Plus 3.5.15、JJWT 0.13.0;启动方式:./mvnw spring-boot:run
|
||||
1. 项目初始化与环境搭建 1.3 设计 MySQL 数据库表结构 设计核心实体表 用户表、学生表、教师表、课程表、学分表 已完成 数据库 stu01_db(MariaDB),用户 stu01/123456;核心表含 sys_user(预置 admin/stu001/tea001,密码 123456);字段约束:学号 / 教师编号 8 位纯数字,专业限 3 类,课程名 2-20 字符,分数 0-100
|
||||
1. 项目初始化与环境搭建 1.4 后端基础配置 配置数据库、跨域、JWT 参数 application.yml、跨域、JWT 密钥 已完成 数据库连接正常;跨域允许 5173 端口(独立 CorsConfig);JWT:32 位密钥、1 小时过期;context-path=/api,端口 8080;MyBatis-Plus 开启下划线转驼峰、SQL 日志
|
||||
2. 用户登录模块开发 2.1 后端用户登录核心逻辑 实现账号密码校验 合法账号、缺少账号 / 密码、错误账号密码 已完成 接口:POST /api/login;校验逻辑:空值 + 存在性 + 密码匹配;返回格式:Result(code/message/data)
|
||||
2. 用户登录模块开发 2.2 JWT 令牌生成与校验 生成 token、校验 token、令牌刷新 合法 token、token 拦截、令牌刷新 已完成 接口:① POST /api/login(返回 token+user);② GET /api/login/refresh(刷新 token);JWT 拦截器放行登录 / 刷新接口,拦截其他 /api/**;token 载荷含 user_type(1 - 学生 / 2 - 教师 / 3 - 管理员)
|
||||
2. 用户登录模块开发 2.4 登录异常处理 统一异常返回格式,明确异常信息 缺少账号 / 密码、错误账号密码、token 无效 / 过期 已完成 全局异常处理器捕获业务 / 参数 / 系统异常;返回统一 Result 格式:业务异常 code=400、系统异常 code=500;支持空参数、账号密码错误、token 异常等场景,提示信息简洁
|
||||
2. 用户登录模块开发 2.5 多用户登录性能基础支持 支持高并发请求 多用户登陆性能测试 已完成 依赖 Spring Boot 3.x 默认线程池
|
||||
3. 学生信息模块开发 3.1 后端学生基础 CRUD 实现学生添加 / 查询接口(支持模糊查询) 学生添加、学生查询(学号 / 姓名 / 专业筛选) 已完成 1. 接口:① POST /api/student/add(添加,校验学号唯一);② GET/POST/api/student/query(查询,学号精确、姓名 / 专业模糊);2. 依赖 JWT 拦截器,需携带合法 token;3. 返回格式统一为 Result,兼容全局异常处理;4. 支持组合条件筛选
|
||||
3. 学生信息模块开发 3.2 学生添加参数校验 校验学号、专业合法性 缺少学号、不合法学号、非法专业 已完成 学号规则:8 位纯数字、非空、唯一;专业限计算机 / 数学 / 英语;用 3.x 参数校验注解
|
||||
3. 学生信息模块开发 3.3 学生查询参数校验 校验 ID 合法性、防 SQL 注入 合法 ID、非法 ID、SQL 注入拦截 未完成 防注入:MyBatis-Plus 参数绑定;ID 校验:非负整数 + 存在性
|
||||
3. 学生信息模块开发 3.5 学生并发测试基础支持 支持添加 / 查询接口并发请求 学生查询 / 添加并发测试 未完成 依赖 3.x 默认配置
|
||||
4. 教师信息模块开发 4.1 后端教师基础 CRUD 实现教师添加 / 更新 / 删除接口 教师添加、更新、删除 未完成 接口路径:POST /api/teacher/add、PUT /api/teacher/update、DELETE /api/teacher/delete
|
||||
4. 教师信息模块开发 4.2 教师删除关联拦截 教师关联课程 / 学分时拦截删除 删除关联拦截 未完成 校验逻辑:查询 course 表关联关系,存在则拦截;可选 @Transactional 事务处理
|
||||
4. 教师信息模块开发 4.3 教师查询防 SQL 注入 校验查询参数、防 SQL 注入 教师查询、SQL 注入拦截 未完成 接口:/api/teacher/query;防注入:MyBatis-Plus 参数绑定
|
||||
4. 教师信息模块开发 4.5 教师查询并发基础支持 确保教师查询接口支持并发请求 老师查询接口并发测试 未完成 依赖 3.x 默认配置
|
||||
5. 课程与学分模块开发 5.1 后端课程 / 学分基础 CRUD 编写课程 / 学分实体 / DAO/Service/Controller 课程添加、学分添加 未完成 接口路径:POST /api/course/add、POST /api/score/add
|
||||
5. 课程与学分模块开发 5.2 课程参数校验 校验课程名称合法性、实现课程查询 课程查询、不合法名称 未完成 课程名规则:2-20 字符(中文 / 数字 / 字母);接口路径:/api/course/query
|
||||
5. 课程与学分模块开发 5.3 学分参数校验 校验分数合法性、实现学分更新 学分更新、错误分数 未完成 分数规则:0-100,超范围返回校验失败;接口路径:PUT /api/score/update
|
||||
5. 课程与学分模块开发 5.5 课程查询并发基础支持 确保课程查询接口支持并发请求 课程查询并发测试 未完成 依赖 3.x 默认配置
|
||||
6. 权限控制模块开发 6.1 JWT 权限标识 令牌中添加用户类型 访问权限 - 未登录、权限标识 已完成 用户类型:1 - 学生、2 - 教师、3 - 管理员;JWT 载荷已包含 user_type
|
||||
6. 权限控制模块开发 6.2 后端接口权限拦截 拦截未登录请求、越权访问请求 越权访问 - 学生 - 老师、学生 - 管理员、老师 - 管理员 未完成 基于 JWT 的 user_type 判断权限;拦截器扩展:学生禁访 /api/teacher/、/api/admin/,教师禁访 /api/admin/**
|
||||
7. 测试适配与代码精简 7.1 测试用例全覆盖核对 逐一核对测试用例,确保功能点无遗漏 用例全覆盖、功能校验 未完成 核对类别:登录 / 学生 / 教师 / 课程学分 / 权限 / 并发 / 注入;适配 3.x、MariaDB
|
||||
7. 测试适配与代码精简 7.2 代码精简 移除冗余逻辑,仅保留测试必需功能 代码简洁、无冗余 未完成 删除冗余代码,简化逻辑;移除无用配置依赖;统一异常处理逻辑
|
||||
7. 测试适配与代码精简 7.3 基础联调 确保后端接口通信正常,核心功能可运行 联调、接口通联 未完成 测试核心接口(登录、增删改查、权限、刷新);前后端跨域正常;数据库读写正常
|
||||
|
||||
三、执行反馈模块(每次会话必填)
|
||||
问题解决:核心是添加了一个springboot的关于validation的starter依赖。
|
||||
确认3.2完成,开始3.3
|
||||
16
stu01/src/main/java/top/awinx/stu01/Stu01Application.java
Normal file
16
stu01/src/main/java/top/awinx/stu01/Stu01Application.java
Normal file
@ -0,0 +1,16 @@
|
||||
package top.awinx.stu01;
|
||||
|
||||
|
||||
import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
@MapperScan("top.awinx.stu01.mapper")
|
||||
public class Stu01Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Stu01Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
49
stu01/src/main/java/top/awinx/stu01/common/Result.java
Normal file
49
stu01/src/main/java/top/awinx/stu01/common/Result.java
Normal file
@ -0,0 +1,49 @@
|
||||
package top.awinx.stu01.common;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 全局统一返回结果
|
||||
* code:200成功,400失败
|
||||
* message:提示信息
|
||||
* data:返回数据
|
||||
*/
|
||||
@Data
|
||||
public class Result<T> {
|
||||
private Integer code;
|
||||
private String message;
|
||||
private T data;
|
||||
|
||||
// 成功返回(无数据)
|
||||
public static <T> Result<T> success(String message) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(200);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 成功返回(带数据)
|
||||
public static <T> Result<T> success(String message, T data) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(200);
|
||||
result.setMessage(message);
|
||||
result.setData(data);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 失败返回
|
||||
public static <T> Result<T> error(String message) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(400);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
|
||||
// 失败返回
|
||||
public static <T> Result<T> error(Integer code, String message) {
|
||||
Result<T> result = new Result<>();
|
||||
result.setCode(code);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
34
stu01/src/main/java/top/awinx/stu01/config/CorsConfig.java
Normal file
34
stu01/src/main/java/top/awinx/stu01/config/CorsConfig.java
Normal file
@ -0,0 +1,34 @@
|
||||
package top.awinx.stu01.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.filter.CorsFilter;
|
||||
|
||||
/**
|
||||
* Spring Boot 3.x跨域配置类(适配前端5173端口)
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig {
|
||||
|
||||
@Bean
|
||||
public CorsFilter corsFilter() {
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
// 允许前端5173端口访问
|
||||
config.addAllowedOrigin("http://localhost:5174");
|
||||
// 允许携带Cookie
|
||||
config.setAllowCredentials(true);
|
||||
// 允许所有请求方法
|
||||
config.addAllowedMethod("*");
|
||||
// 允许所有请求头
|
||||
config.addAllowedHeader("*");
|
||||
// 暴露响应头(方便前端获取JWT令牌)
|
||||
config.addExposedHeader("Authorization");
|
||||
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
// 所有接口都允许跨域
|
||||
source.registerCorsConfiguration("/**", config);
|
||||
return new CorsFilter(source);
|
||||
}
|
||||
}
|
||||
54
stu01/src/main/java/top/awinx/stu01/config/JwtConfig.java
Normal file
54
stu01/src/main/java/top/awinx/stu01/config/JwtConfig.java
Normal file
@ -0,0 +1,54 @@
|
||||
package top.awinx.stu01.config;
|
||||
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* JWT配置类(适配jjwt 0.13.0)
|
||||
*/
|
||||
@Configuration
|
||||
public class JwtConfig {
|
||||
|
||||
// 从yml配置中读取密钥
|
||||
@Value("${jwt.secret}")
|
||||
private String secret;
|
||||
|
||||
// 从yml配置中读取过期时间
|
||||
@Value("${jwt.expire}")
|
||||
private long expire;
|
||||
|
||||
// 从yml配置中读取请求头key
|
||||
@Value("${jwt.header}")
|
||||
private String header;
|
||||
|
||||
// 从yml配置中读取令牌前缀
|
||||
@Value("${jwt.prefix}")
|
||||
private String prefix;
|
||||
|
||||
/**
|
||||
* 创建JWT签名密钥(jjwt 0.13.0要求密钥长度≥256位)
|
||||
*/
|
||||
@Bean
|
||||
public SecretKey jwtSecretKey() {
|
||||
// 将字符串密钥转换为SecretKey对象
|
||||
return Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// 提供getter方法,方便其他类获取配置
|
||||
public long getExpire() {
|
||||
return expire;
|
||||
}
|
||||
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
}
|
||||
32
stu01/src/main/java/top/awinx/stu01/config/WebMvcConfig.java
Normal file
32
stu01/src/main/java/top/awinx/stu01/config/WebMvcConfig.java
Normal file
@ -0,0 +1,32 @@
|
||||
package top.awinx.stu01.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import top.awinx.stu01.interceptor.JwtInterceptor;
|
||||
|
||||
/**
|
||||
* SpringMVC配置:注册JWT拦截器+跨域配置(适配Spring Boot 3.x)
|
||||
*/
|
||||
@Configuration
|
||||
public class WebMvcConfig implements WebMvcConfigurer {
|
||||
|
||||
/**
|
||||
* 注册JWT拦截器Bean
|
||||
*/
|
||||
@Bean
|
||||
public JwtInterceptor jwtInterceptor() {
|
||||
return new JwtInterceptor();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置拦截规则:放行登录接口,拦截其他/api/**接口
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
registry.addInterceptor(jwtInterceptor())
|
||||
.addPathPatterns("/**") // 拦截所有/api开头的接口
|
||||
.excludePathPatterns("/login"); // 放行登录接口
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
package top.awinx.stu01.controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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 top.awinx.stu01.common.Result;
|
||||
import top.awinx.stu01.config.JwtConfig;
|
||||
import top.awinx.stu01.dto.LoginRequest;
|
||||
import top.awinx.stu01.entity.SysUser;
|
||||
import top.awinx.stu01.service.SysUserService;
|
||||
import top.awinx.stu01.utils.JwtUtil;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import top.awinx.stu01.exception.BusinessException;
|
||||
|
||||
/**
|
||||
* 登录接口Controller
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/login")
|
||||
public class LoginController {
|
||||
|
||||
@Resource
|
||||
private SysUserService sysUserService;
|
||||
|
||||
@Resource
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Resource
|
||||
private JwtConfig jwtConfig;
|
||||
|
||||
/**
|
||||
* 登录接口(POST请求,接收JSON参数)
|
||||
* @param loginRequest JSON格式的登录参数
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@PostMapping("")
|
||||
public Result<Object> login(@Valid @RequestBody LoginRequest loginRequest) {
|
||||
// 提取账号密码
|
||||
String username = loginRequest.getUsername();
|
||||
String password = loginRequest.getPassword();
|
||||
|
||||
// 调用Service校验账号密码
|
||||
SysUser sysUser = sysUserService.loginCheck(username, password);
|
||||
|
||||
// 校验失败
|
||||
if (sysUser == null) {
|
||||
// throw new BusinessException("账号或密码错误");
|
||||
return Result.error("账号或密码错误");
|
||||
}
|
||||
|
||||
// 生成JWT令牌
|
||||
String token = jwtUtil.generateToken(sysUser);
|
||||
|
||||
// 组装返回数据(token+基础用户信息)
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
resultMap.put("token", token);
|
||||
resultMap.put("user", sysUser);
|
||||
|
||||
// 校验成功
|
||||
return Result.success("登录成功", resultMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 登陆令牌刷新接口(GET请求,无参数)
|
||||
* @return 统一返回结果
|
||||
*/
|
||||
@GetMapping("/refresh")
|
||||
public Result<Object> refresh(HttpServletRequest request) {
|
||||
String oldToken = request.getHeader(jwtConfig.getHeader());
|
||||
if (oldToken != null && oldToken.startsWith(jwtConfig.getPrefix())){
|
||||
oldToken = oldToken.substring(jwtConfig.getPrefix().length());
|
||||
oldToken = oldToken.trim();
|
||||
}
|
||||
if (oldToken == null || !jwtUtil.validateToken(oldToken)) {
|
||||
throw new BusinessException("请先登录或token已过期");
|
||||
}
|
||||
SysUser sysUser = jwtUtil.getUserFromToken(oldToken);
|
||||
String newToken = jwtUtil.generateToken(sysUser);
|
||||
return Result.success("刷新成功", newToken);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package top.awinx.stu01.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
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 top.awinx.stu01.common.Result;
|
||||
import top.awinx.stu01.dto.StudentAddDTO;
|
||||
import top.awinx.stu01.dto.StudentQueryDTO;
|
||||
import top.awinx.stu01.entity.Student;
|
||||
import top.awinx.stu01.service.StudentService;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生CRUD控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/student")
|
||||
public class StudentController {
|
||||
|
||||
@Resource
|
||||
private StudentService studentService;
|
||||
|
||||
/**
|
||||
* 添加学生接口
|
||||
* 请求方式:POST
|
||||
* 接口路径:/api/student/add
|
||||
*/
|
||||
@PostMapping("/add")
|
||||
public Result<Student> addStudent(@Valid @RequestBody StudentAddDTO addDTO) {
|
||||
Student student = studentService.addStudent(addDTO);
|
||||
return Result.success("添加学生成功", student);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询学生接口(兼容GET/POST,支持模糊查询)
|
||||
* 请求方式:GET/POST
|
||||
* 接口路径:/api/student/query
|
||||
*/
|
||||
@GetMapping("/query")
|
||||
public Result<List<Student>> queryStudentByGet(StudentQueryDTO queryDTO) {
|
||||
List<Student> studentList = studentService.queryStudent(queryDTO);
|
||||
return Result.success("查询成功", studentList);
|
||||
}
|
||||
|
||||
@PostMapping("/query")
|
||||
public Result<List<Student>> queryStudentByPost(@RequestBody StudentQueryDTO queryDTO) {
|
||||
List<Student> studentList = studentService.queryStudent(queryDTO);
|
||||
return Result.success("查询成功", studentList);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package top.awinx.stu01.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 测试接口,验证数据库和跨域配置
|
||||
*/
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@GetMapping("/test")
|
||||
public String test() {
|
||||
return "后端配置生效,前端可跨域访问!";
|
||||
}
|
||||
}
|
||||
15
stu01/src/main/java/top/awinx/stu01/dto/LoginRequest.java
Normal file
15
stu01/src/main/java/top/awinx/stu01/dto/LoginRequest.java
Normal file
@ -0,0 +1,15 @@
|
||||
package top.awinx.stu01.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 登录请求参数DTO(接收JSON格式参数)
|
||||
*/
|
||||
@Data
|
||||
public class LoginRequest {
|
||||
@NotBlank(message = "用户名不能为空")
|
||||
private String username; // 登录账号
|
||||
@NotBlank(message = "密码不能为空")
|
||||
private String password; // 登录密码
|
||||
}
|
||||
39
stu01/src/main/java/top/awinx/stu01/dto/StudentAddDTO.java
Normal file
39
stu01/src/main/java/top/awinx/stu01/dto/StudentAddDTO.java
Normal file
@ -0,0 +1,39 @@
|
||||
package top.awinx.stu01.dto;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 添加学生请求参数DTO
|
||||
*/
|
||||
@Data
|
||||
public class StudentAddDTO {
|
||||
|
||||
/**
|
||||
* 学号(8位纯数字)
|
||||
*/
|
||||
@NotBlank(message = "学号不能为空")
|
||||
@Pattern(regexp = "^\\d{8}$", message = "学号必须为8位纯数字,不能包含特殊字符")
|
||||
private String stuNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
@NotBlank(message = "学生姓名不能为空")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 专业(计算机/数学/英语)
|
||||
*/
|
||||
@NotBlank(message = "专业不能为空")
|
||||
@Pattern(regexp = "^(计算机|数学|英语)$", message = "专业仅支持:计算机、数学、英语")
|
||||
private String major;
|
||||
|
||||
/**
|
||||
* 关联用户ID
|
||||
*/
|
||||
@NotNull(message = "关联用户ID不能为空")
|
||||
private Long userId;
|
||||
}
|
||||
24
stu01/src/main/java/top/awinx/stu01/dto/StudentQueryDTO.java
Normal file
24
stu01/src/main/java/top/awinx/stu01/dto/StudentQueryDTO.java
Normal file
@ -0,0 +1,24 @@
|
||||
package top.awinx.stu01.dto;
|
||||
|
||||
import jakarta.validation.constraints.Min;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class StudentQueryDTO {
|
||||
|
||||
/**
|
||||
* 学号(精确匹配)
|
||||
*/
|
||||
private String stuNo;
|
||||
|
||||
/**
|
||||
* 姓名(模糊匹配)
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 专业(模糊匹配)
|
||||
*/
|
||||
private String major;
|
||||
|
||||
}
|
||||
53
stu01/src/main/java/top/awinx/stu01/entity/Student.java
Normal file
53
stu01/src/main/java/top/awinx/stu01/entity/Student.java
Normal file
@ -0,0 +1,53 @@
|
||||
package top.awinx.stu01.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 学生实体类:对应student表
|
||||
*/
|
||||
@Data
|
||||
@TableName("student") // 关联数据库表名
|
||||
public class Student {
|
||||
|
||||
/**
|
||||
* 主键ID(自增)
|
||||
*/
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 学号(8位纯数字,唯一)
|
||||
*/
|
||||
@TableField("stu_no")
|
||||
private String stuNo;
|
||||
|
||||
/**
|
||||
* 学生姓名
|
||||
*/
|
||||
@TableField("name")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 专业(计算机/数学/英语)
|
||||
*/
|
||||
@TableField("major")
|
||||
private String major;
|
||||
|
||||
/**
|
||||
* 关联用户ID(外键)
|
||||
*/
|
||||
@TableField("user_id")
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 创建时间(默认当前时间)
|
||||
*/
|
||||
@TableField("create_time")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
20
stu01/src/main/java/top/awinx/stu01/entity/SysUser.java
Normal file
20
stu01/src/main/java/top/awinx/stu01/entity/SysUser.java
Normal file
@ -0,0 +1,20 @@
|
||||
package top.awinx.stu01.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 系统用户实体(对应sys_user表)
|
||||
*/
|
||||
@Data
|
||||
@TableName("sys_user") // 关联数据库表名
|
||||
public class SysUser {
|
||||
@TableId(type = IdType.AUTO) // 主键自增
|
||||
private Integer id; // 用户ID
|
||||
private String username; // 登录账号
|
||||
private String password; // 登录密码(测试用明文,无需加密)
|
||||
private Integer userType; // 用户类型:1-学生 2-教师 3-管理员
|
||||
private String realName; // 真实姓名
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package top.awinx.stu01.exception;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class BusinessException extends RuntimeException {
|
||||
private final int code = 400;
|
||||
private final String message;
|
||||
public BusinessException(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package top.awinx.stu01.exception;
|
||||
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
|
||||
import top.awinx.stu01.common.Result;
|
||||
|
||||
/**
|
||||
* 全局异常处理器:统一捕获所有接口异常,返回标准Result格式
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
/**
|
||||
* 捕获业务异常(如账号密码错误、token无效等)
|
||||
*/
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public Result<Object> handleBusinessException(BusinessException e) {
|
||||
return Result.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获参数校验异常(如@NotBlank、@Pattern触发的异常)
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public Result<Object> handleValidException(MethodArgumentNotValidException e) {
|
||||
// 获取第一个校验失败的字段提示信息
|
||||
String message = e.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
|
||||
return Result.error(400, message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 捕获JSON解析异常(如JSON字段名称不匹配、JSON语法错误等)
|
||||
*/
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public Result<Object> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
|
||||
// 判断是否是JSON解析错误
|
||||
Throwable rootCause = e.getRootCause();
|
||||
if (rootCause instanceof JsonParseException) {
|
||||
return Result.error(400, "JSON格式错误,请检查:字段名需用双引号、括号匹配、无多余逗号/特殊字符");
|
||||
} else {
|
||||
return Result.error(400, "请求体格式错误,请检查JSON语法");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获数据库约束异常(如非空字段为空、字段长度超出限制等)
|
||||
*/
|
||||
@ExceptionHandler(DataIntegrityViolationException.class)
|
||||
public Result<Object> handleDataIntegrityException(DataIntegrityViolationException e) {
|
||||
String msg = e.getMessage();
|
||||
if (msg.contains("constraint")) {
|
||||
if (msg.contains("primary")){
|
||||
return Result.error(400, "主键冲突,请检查");
|
||||
} else if (msg.contains("unique")) {
|
||||
return Result.error(400, "唯一约束冲突,请检查");
|
||||
} else {
|
||||
return Result.error(400, "数据库约束冲突,请检查");
|
||||
}
|
||||
} else {
|
||||
return Result.error(400, "数据库操作失败,请检查");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 捕获系统异常(兜底处理)
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public Result<Object> handleSystemException(Exception e) {
|
||||
// 系统异常返回通用提示(避免暴露敏感信息)
|
||||
e.printStackTrace();
|
||||
return Result.error(500, "服务器内部错误,请稍后重试");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package top.awinx.stu01.interceptor;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import top.awinx.stu01.common.Result;
|
||||
import top.awinx.stu01.config.JwtConfig;
|
||||
import top.awinx.stu01.utils.JwtUtil;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* JWT拦截器:校验请求头中的token合法性
|
||||
*/
|
||||
public class JwtInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Resource
|
||||
private JwtUtil jwtUtil;
|
||||
@Resource
|
||||
private JwtConfig jwtConfig;
|
||||
|
||||
/**
|
||||
* 预处理:请求到达Controller前校验token
|
||||
*/
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// 1. 获取请求头中的token
|
||||
String token = request.getHeader(jwtConfig.getHeader());
|
||||
String prefix = jwtConfig.getPrefix();
|
||||
if (token != null && token.startsWith(prefix)) {
|
||||
token = token.substring(prefix.length());
|
||||
token = token.trim();
|
||||
}
|
||||
|
||||
// 2. 校验token是否存在且合法
|
||||
if (token == null || !jwtUtil.validateToken(token)) {
|
||||
// 3. 非法token/无token,返回400错误
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
Result<Object> result = Result.error("请先登录或token已过期");
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write(new ObjectMapper().writeValueAsString(result));
|
||||
writer.flush();
|
||||
writer.close();
|
||||
return false; // 拦截请求
|
||||
}
|
||||
|
||||
// 4. token合法,放行请求
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package top.awinx.stu01.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import top.awinx.stu01.entity.Student;
|
||||
|
||||
/**
|
||||
* 学生Mapper:继承MyBatis-Plus BaseMapper,无需自定义SQL
|
||||
*/
|
||||
@Mapper
|
||||
public interface StudentMapper extends BaseMapper<Student> {
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package top.awinx.stu01.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import top.awinx.stu01.entity.SysUser;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* MyBatis-Plus Mapper(DAO层)
|
||||
* BaseMapper已封装CRUD,无需手动写SQL
|
||||
*/
|
||||
@Mapper // 标识为MyBatis Mapper,Spring扫描
|
||||
public interface SysUserMapper extends BaseMapper<SysUser> {
|
||||
// 无需新增方法,BaseMapper已包含:selectOne、selectList等
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package top.awinx.stu01.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.awinx.stu01.dto.StudentAddDTO;
|
||||
import top.awinx.stu01.dto.StudentQueryDTO;
|
||||
import top.awinx.stu01.entity.Student;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生Service接口
|
||||
*/
|
||||
public interface StudentService extends IService<Student> {
|
||||
|
||||
/**
|
||||
* 添加学生
|
||||
* @param addDTO 添加参数
|
||||
* @return 添加后的学生信息
|
||||
*/
|
||||
Student addStudent(StudentAddDTO addDTO);
|
||||
|
||||
/**
|
||||
* 模糊查询学生(支持学号精确、姓名/专业模糊)
|
||||
* @param queryDTO 查询参数
|
||||
* @return 学生列表
|
||||
*/
|
||||
List<Student> queryStudent(StudentQueryDTO queryDTO);
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package top.awinx.stu01.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import top.awinx.stu01.entity.SysUser;
|
||||
|
||||
/**
|
||||
* 用户服务接口
|
||||
*/
|
||||
public interface SysUserService extends IService<SysUser> {
|
||||
// 登录校验方法:返回用户信息(null表示校验失败)
|
||||
SysUser loginCheck(String username, String password);
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package top.awinx.stu01.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.awinx.stu01.dto.StudentAddDTO;
|
||||
import top.awinx.stu01.dto.StudentQueryDTO;
|
||||
import top.awinx.stu01.entity.Student;
|
||||
import top.awinx.stu01.exception.BusinessException;
|
||||
import top.awinx.stu01.mapper.StudentMapper;
|
||||
import top.awinx.stu01.service.StudentService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 学生Service实现类
|
||||
*/
|
||||
@Service
|
||||
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements StudentService {
|
||||
|
||||
/**
|
||||
* 添加学生:校验学号唯一性,转换DTO为实体并保存
|
||||
*/
|
||||
@Override
|
||||
public Student addStudent(StudentAddDTO addDTO) {
|
||||
// 1. 校验学号是否已存在
|
||||
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(Student::getStuNo, addDTO.getStuNo());
|
||||
if (this.count(wrapper) > 0) {
|
||||
throw new BusinessException("学号" + addDTO.getStuNo() + "已存在");
|
||||
}
|
||||
|
||||
// 2. DTO转换为实体
|
||||
Student student = new Student();
|
||||
student.setStuNo(addDTO.getStuNo());
|
||||
student.setName(addDTO.getName());
|
||||
student.setMajor(addDTO.getMajor());
|
||||
student.setUserId(addDTO.getUserId());
|
||||
|
||||
// 3. 保存学生信息
|
||||
this.save(student);
|
||||
return student;
|
||||
}
|
||||
|
||||
/**
|
||||
* 模糊查询学生:学号精确匹配,姓名/专业模糊匹配
|
||||
*/
|
||||
@Override
|
||||
public List<Student> queryStudent(StudentQueryDTO queryDTO) {
|
||||
LambdaQueryWrapper<Student> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 学号:精确匹配
|
||||
if (queryDTO.getStuNo() != null && !queryDTO.getStuNo().isEmpty()) {
|
||||
wrapper.eq(Student::getStuNo, queryDTO.getStuNo());
|
||||
}
|
||||
|
||||
// 姓名:模糊匹配(包含)
|
||||
if (queryDTO.getName() != null && !queryDTO.getName().isEmpty()) {
|
||||
wrapper.like(Student::getName, queryDTO.getName());
|
||||
}
|
||||
|
||||
// 专业:模糊匹配(包含)
|
||||
if (queryDTO.getMajor() != null && !queryDTO.getMajor().isEmpty()) {
|
||||
wrapper.like(Student::getMajor, queryDTO.getMajor());
|
||||
}
|
||||
|
||||
// 按创建时间降序排列
|
||||
wrapper.orderByDesc(Student::getCreateTime);
|
||||
|
||||
// 查询并返回结果
|
||||
return this.list(wrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package top.awinx.stu01.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
import top.awinx.stu01.entity.SysUser;
|
||||
import top.awinx.stu01.mapper.SysUserMapper;
|
||||
import top.awinx.stu01.service.SysUserService;
|
||||
|
||||
/**
|
||||
* 用户服务实现类(核心业务逻辑)
|
||||
*/
|
||||
@Service // 标识为Spring服务Bean
|
||||
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
|
||||
|
||||
/**
|
||||
* 登录校验逻辑:
|
||||
* 1. 校验账号非空
|
||||
* 2. 校验密码非空
|
||||
* 3. 查询数据库是否存在该账号
|
||||
* 4. 校验密码是否匹配
|
||||
*/
|
||||
@Override
|
||||
public SysUser loginCheck(String username, String password) {
|
||||
// 1. 空值校验
|
||||
if (username == null || username.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (password == null || password.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 2. 根据账号查询用户(LambdaQueryWrapper避免SQL注入)
|
||||
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(SysUser::getUsername, username); // 等值查询username
|
||||
SysUser sysUser = this.baseMapper.selectOne(queryWrapper);
|
||||
|
||||
// 3. 校验用户存在且密码匹配(测试用明文对比)
|
||||
if (sysUser == null || !password.equals(sysUser.getPassword())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 4. 校验通过,返回用户信息(后续生成JWT用)
|
||||
return sysUser;
|
||||
}
|
||||
}
|
||||
86
stu01/src/main/java/top/awinx/stu01/utils/JwtUtil.java
Normal file
86
stu01/src/main/java/top/awinx/stu01/utils/JwtUtil.java
Normal file
@ -0,0 +1,86 @@
|
||||
package top.awinx.stu01.utils;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
import top.awinx.stu01.config.JwtConfig;
|
||||
import top.awinx.stu01.entity.SysUser;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* JWT工具类(适配jjwt 0.13.0)
|
||||
* 功能:生成token、解析token、校验token
|
||||
*/
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
@Resource
|
||||
private JwtConfig jwtConfig;
|
||||
|
||||
/**
|
||||
* 生成JWT令牌
|
||||
* @param sysUser 登录成功的用户信息
|
||||
* @return token字符串
|
||||
*/
|
||||
public String generateToken(SysUser sysUser) {
|
||||
// 生成密钥(适配jjwt 0.13.0,需32位密钥)
|
||||
SecretKey secretKey = jwtConfig.jwtSecretKey();
|
||||
|
||||
// 构建token,载荷包含user_id、user_type、username
|
||||
return Jwts.builder()
|
||||
.claim("user_id", sysUser.getId()) // 用户ID
|
||||
.claim("user_type", sysUser.getUserType()) // 用户类型(1-学生/2-教师/3-管理员)
|
||||
.claim("username", sysUser.getUsername()) // 用户名
|
||||
.issuedAt(new Date()) // 签发时间
|
||||
.expiration(new Date(System.currentTimeMillis() + jwtConfig.getExpire() * 1000)) // 过期时间
|
||||
.signWith(secretKey) // 签名
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析token,获取载荷信息
|
||||
* @param token JWT令牌
|
||||
* @return 载荷Claims
|
||||
*/
|
||||
public Claims parseToken(String token) {
|
||||
SecretKey secretKey = jwtConfig.jwtSecretKey();
|
||||
return Jwts.parser()
|
||||
.verifyWith(secretKey)
|
||||
.build()
|
||||
.parseSignedClaims(token)
|
||||
.getPayload();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token中的用户信息
|
||||
* @param token
|
||||
* @return 用户信息
|
||||
*/
|
||||
public SysUser getUserFromToken(String token) {
|
||||
Claims claims = parseToken(token);
|
||||
SysUser sysUser = new SysUser();
|
||||
sysUser.setId(claims.get("user_id", Integer.class));
|
||||
sysUser.setUserType(claims.get("user_type", Integer.class));
|
||||
sysUser.setUsername(claims.get("username", String.class));
|
||||
return sysUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验token是否合法(未过期+签名正确)
|
||||
* @param token JWT令牌
|
||||
* @return true-合法,false-非法
|
||||
*/
|
||||
public boolean validateToken(String token) {
|
||||
try {
|
||||
Claims claims = parseToken(token);
|
||||
// 校验是否过期
|
||||
return !claims.getExpiration().before(new Date());
|
||||
} catch (Exception e) {
|
||||
// 签名错误、过期、格式错误等均返回false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
46
stu01/src/main/resources/application.yml
Normal file
46
stu01/src/main/resources/application.yml
Normal file
@ -0,0 +1,46 @@
|
||||
# 服务器配置
|
||||
server:
|
||||
port: 8080 # 后端端口,与前端代理配置一致
|
||||
servlet:
|
||||
context-path: /api # 接口前缀,适配前端跨域代理
|
||||
|
||||
# 数据源配置(适配MariaDB,兼容MySQL驱动)
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver # Spring Boot 3.x推荐驱动类
|
||||
url: jdbc:mysql://localhost:3306/stu01_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: stu01 # 自定义数据库用户
|
||||
password: 123456 # 数据库用户密码
|
||||
# 跨域配置(配合CorsConfig类,双重保障)
|
||||
web:
|
||||
cors:
|
||||
allowed-origins: http://localhost:5174 # 前端实际端口
|
||||
allowed-methods: "*"
|
||||
allowed-headers: "*"
|
||||
allow-credentials: true
|
||||
|
||||
# MyBatis-Plus配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true # 下划线转驼峰
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志(测试用)
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: AUTO # 主键自增
|
||||
type-aliases-package: top.awinx.stu01.entity # 实体类包扫描
|
||||
# mapper-locations: classpath:mapper/*.xml # Mapper XML文件扫描
|
||||
# mybatis:
|
||||
# mapperLocations: classpath:mapper/*.xml
|
||||
|
||||
logging:
|
||||
level:
|
||||
top.awinx.stu01.mapper: debug
|
||||
com.baomidou.mybatisplus: debug
|
||||
org.mybatis: debug
|
||||
|
||||
# JWT自定义配置(适配jjwt 0.13.0)
|
||||
jwt:
|
||||
secret: abcdefghijklmnopqrstuvwxyz123456 # 32位密钥(测试用,生产需替换)
|
||||
expire: 3600000 # 过期时间:1小时(毫秒)
|
||||
header: Authorization # 请求头中JWT令牌的key
|
||||
prefix: Bearer # 令牌前缀
|
||||
7
stu01/src/main/resources/mapper/SysUserMapper.xml
Normal file
7
stu01/src/main/resources/mapper/SysUserMapper.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.awinx.stu01.mapper.SysUserMapper">
|
||||
<!-- 这个文件可以是空的,MyBatis-Plus会动态生成SQL -->
|
||||
<!-- 如果需要自定义SQL,可以在这里添加 -->
|
||||
</mapper>
|
||||
@ -0,0 +1,13 @@
|
||||
package top.awinx.stu01;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class Stu01ApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
46
stu01/target/classes/application.yml
Normal file
46
stu01/target/classes/application.yml
Normal file
@ -0,0 +1,46 @@
|
||||
# 服务器配置
|
||||
server:
|
||||
port: 8080 # 后端端口,与前端代理配置一致
|
||||
servlet:
|
||||
context-path: /api # 接口前缀,适配前端跨域代理
|
||||
|
||||
# 数据源配置(适配MariaDB,兼容MySQL驱动)
|
||||
spring:
|
||||
datasource:
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver # Spring Boot 3.x推荐驱动类
|
||||
url: jdbc:mysql://localhost:3306/stu01_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||
username: stu01 # 自定义数据库用户
|
||||
password: 123456 # 数据库用户密码
|
||||
# 跨域配置(配合CorsConfig类,双重保障)
|
||||
web:
|
||||
cors:
|
||||
allowed-origins: http://localhost:5174 # 前端实际端口
|
||||
allowed-methods: "*"
|
||||
allowed-headers: "*"
|
||||
allow-credentials: true
|
||||
|
||||
# MyBatis-Plus配置
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
map-underscore-to-camel-case: true # 下划线转驼峰
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印SQL日志(测试用)
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: AUTO # 主键自增
|
||||
type-aliases-package: top.awinx.stu01.entity # 实体类包扫描
|
||||
# mapper-locations: classpath:mapper/*.xml # Mapper XML文件扫描
|
||||
# mybatis:
|
||||
# mapperLocations: classpath:mapper/*.xml
|
||||
|
||||
logging:
|
||||
level:
|
||||
top.awinx.stu01.mapper: debug
|
||||
com.baomidou.mybatisplus: debug
|
||||
org.mybatis: debug
|
||||
|
||||
# JWT自定义配置(适配jjwt 0.13.0)
|
||||
jwt:
|
||||
secret: abcdefghijklmnopqrstuvwxyz123456 # 32位密钥(测试用,生产需替换)
|
||||
expire: 3600000 # 过期时间:1小时(毫秒)
|
||||
header: Authorization # 请求头中JWT令牌的key
|
||||
prefix: Bearer # 令牌前缀
|
||||
7
stu01/target/classes/mapper/SysUserMapper.xml
Normal file
7
stu01/target/classes/mapper/SysUserMapper.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="top.awinx.stu01.mapper.SysUserMapper">
|
||||
<!-- 这个文件可以是空的,MyBatis-Plus会动态生成SQL -->
|
||||
<!-- 如果需要自定义SQL,可以在这里添加 -->
|
||||
</mapper>
|
||||
@ -0,0 +1,11 @@
|
||||
top/awinx/stu01/config/JwtConfig.class
|
||||
top/awinx/stu01/service/impl/SysUserServiceImpl.class
|
||||
top/awinx/stu01/config/CorsConfig.class
|
||||
top/awinx/stu01/dto/LoginRequest.class
|
||||
top/awinx/stu01/entity/SysUser.class
|
||||
top/awinx/stu01/Stu01Application.class
|
||||
top/awinx/stu01/controller/LoginController.class
|
||||
top/awinx/stu01/service/SysUserService.class
|
||||
top/awinx/stu01/mapper/SysUserMapper.class
|
||||
top/awinx/stu01/controller/TestController.class
|
||||
top/awinx/stu01/common/Result.class
|
||||
@ -0,0 +1,23 @@
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/Stu01Application.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/common/Result.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/config/CorsConfig.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/config/JwtConfig.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/config/WebMvcConfig.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/controller/LoginController.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/controller/StudentController.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/controller/TestController.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/dto/LoginRequest.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/dto/StudentAddDTO.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/dto/StudentQueryDTO.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/entity/Student.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/entity/SysUser.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/exception/BusinessException.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/exception/GlobalExceptionHandler.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/interceptor/JwtInterceptor.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/mapper/StudentMapper.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/mapper/SysUserMapper.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/service/StudentService.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/service/SysUserService.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/service/impl/StudentServiceImpl.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/service/impl/SysUserServiceImpl.java
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/main/java/top/awinx/stu01/utils/JwtUtil.java
|
||||
@ -0,0 +1 @@
|
||||
top/awinx/stu01/Stu01ApplicationTests.class
|
||||
@ -0,0 +1 @@
|
||||
/home/awinx/code/java/lerning/java_web/stu01/src/test/java/top/awinx/stu01/Stu01ApplicationTests.java
|
||||
Loading…
Reference in New Issue
Block a user