diff --git a/test05/USER_ROLE_GUIDE.md b/test05/USER_ROLE_GUIDE.md new file mode 100644 index 0000000..33cd103 --- /dev/null +++ b/test05/USER_ROLE_GUIDE.md @@ -0,0 +1,130 @@ +# 用户角色访问控制指南 + +## 用户名格式规则 + +- **管理员**: `a_<姓名>` (例如: `a_admin`, `a_zhangsan`) +- **老师**: `t_<姓名>` (例如: `t_teacher`, `t_lisi`) +- **学生**: `s_<姓名>` (例如: `s_student`, `s_wangwu`) + +## 权限说明 + +### 管理员权限 +- 可以访问所有API端点 +- 可以创建老师用户 +- 可以创建学生用户 +- 可以创建管理员用户 + +### 老师权限 +- 可以访问老师端API (`/teacherAction/**`) +- 可以访问学生端API (`/studentAction/**`) +- 可以创建学生用户 +- 不能创建老师或管理员用户 + +### 学生权限 +- 只能访问学生端API (`/studentAction/**`) +- 不能创建任何用户 + +## API端点 + +### 用户管理API (`/userManagementAction/**`) + +#### 创建老师用户 +``` +POST /userManagementAction/createTeacher +参数: username, password +权限: 仅管理员 +``` + +#### 创建学生用户 +``` +POST /userManagementAction/createStudent +参数: username, password +权限: 管理员和老师 +``` + +#### 创建管理员用户 +``` +POST /userManagementAction/createAdmin +参数: username, password +权限: 仅管理员 +``` + +### 课程管理API示例 (`/courseAction/**`) + +#### 选课API (学生端) +``` +POST /courseAction/selectCourse +参数: courseId +权限: 学生 +状态: 待实现 +``` + +#### 创建课程API (老师端) +``` +POST /courseAction/createCourse +参数: courseName, courseDescription +权限: 老师 +状态: 待实现 +``` + +#### 获取课程列表 +``` +GET /courseAction/getCourses +参数: page, pageSize +权限: 所有用户 +状态: 待实现 +``` + +## 使用示例 + +### 1. 管理员登录 +```bash +# 假设已存在管理员用户 a_admin +POST /userAction/login +{ + "username": "a_admin", + "password": "password" +} +``` + +### 2. 管理员创建老师用户 +```bash +POST /userManagementAction/createTeacher +Headers: token: <登录返回的JWT> +{ + "username": "t_zhangsan", + "password": "password123" +} +``` + +### 3. 老师创建学生用户 +```bash +POST /userManagementAction/createStudent +Headers: token: <老师登录返回的JWT> +{ + "username": "s_lisi", + "password": "password123" +} +``` + +## 注意事项 + +1. 所有API调用都需要在请求头中包含有效的JWT token +2. 用户名必须严格按照格式要求,否则会被拒绝 +3. 拦截器会自动验证用户角色权限 +4. 课程相关API目前只是空实现,需要创建相应的数据库表后才能完成功能 + +## 数据库表需求 + +为了完整实现课程功能,需要创建以下表: + +1. **课程表** (courses) + - id, course_name, course_description, teacher_id, create_time, etc. + +2. **选课表** (student_courses) + - id, student_id, course_id, select_time, etc. + +3. **课程老师映射表** (如果需要多对多关系) + - id, course_id, teacher_id, etc. + +这些表创建后,就可以完成课程管理API的具体实现了。 \ No newline at end of file diff --git a/test05/pom.xml b/test05/pom.xml index dadd0ae..4f1336d 100644 --- a/test05/pom.xml +++ b/test05/pom.xml @@ -1,148 +1,231 @@ - 4.0.0 + + org.springframework.boot spring-boot-starter-parent - 3.4.0 - + 3.5.0 + + + top.awin-x test05 0.0.1-SNAPSHOT test05 - test05 - + Test project for Spring Boot (JDK 25) + https://github.com/awin-x/test05 + + - + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + - + + awin-x + awin-x + awin-x@example.com + + Developer + + +8 + + + - - - - + scm:git:https://github.com/awin-x/test05.git + scm:git:git@github.com:awin-x/test05.git + https://github.com/awin-x/test05 + HEAD + + - 21 + 25 + UTF-8 + UTF-8 + + + 3.0.4 + 1.2.24 + 3.46.1.0 + 8.4.0 + 0.12.6 + 2.0.53 + 1.18.38 - - - - + org.springframework.boot spring-boot-starter-web + org.mybatis.spring.boot mybatis-spring-boot-starter - 3.0.4 + ${mybatis-spring-boot.version} + + + com.alibaba + druid-spring-boot-3-starter + ${druid.version} + + + + + org.xerial + sqlite-jdbc + ${sqlite-jdbc.version} + + + + + com.mysql + mysql-connector-j + ${mysql-connector.version} + runtime + + + + + org.projectlombok + lombok + ${lombok.version} + true + + + + + io.jsonwebtoken + jjwt + ${jjwt.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson2.version} + + + + + org.springframework.boot + spring-boot-starter-aop + + + org.springframework.boot spring-boot-starter-test test + org.mybatis.spring.boot mybatis-spring-boot-starter-test - 3.0.4 + ${mybatis-spring-boot.version} test - - - com.alibaba - druid-spring-boot-3-starter - 1.2.24 - - - - org.xerial - sqlite-jdbc - 3.47.1.0 - - - - mysql - mysql-connector-java - 8.0.33 - - - - org.projectlombok - lombok - true - - - - io.jsonwebtoken - jjwt - 0.12.3 - - - - com.alibaba.fastjson2 - fastjson2 - 2.0.53 - - - org.springframework.boot - spring-boot-starter-aop - - - - org.realityforge.org.jetbrains.annotations - org.jetbrains.annotations - 1.7.0 - - - - - - - - - - - - - - - + org.springframework.boot spring-boot-maven-plugin + + + + dev + + + + + org.projectlombok + lombok + + + + + org.apache.maven.plugins maven-compiler-plugin - 3.11.0 + 3.14.1 - 25 - 25 + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + org.projectlombok lombok - 1.18.24 + ${lombok.version} + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.3.1 + + false + false + + + + diff --git a/test05/src/main/java/test05/config/WebConfig.java b/test05/src/main/java/test05/config/WebConfig.java index 78fcf91..ac6b897 100644 --- a/test05/src/main/java/test05/config/WebConfig.java +++ b/test05/src/main/java/test05/config/WebConfig.java @@ -2,21 +2,59 @@ package test05.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import test05.interceptor.LoginCheckInterceptor; + +/** + * 配置类 + */ @Configuration public class WebConfig implements WebMvcConfigurer { private final LoginCheckInterceptor loginCheckInterceptor; + private final Environment environment; + // 构造函数注入,登陆检查拦截器 @Autowired - WebConfig(LoginCheckInterceptor loginCheckInterceptor){ + WebConfig(LoginCheckInterceptor loginCheckInterceptor, Environment environment){ this.loginCheckInterceptor = loginCheckInterceptor; + this.environment = environment; } @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(loginCheckInterceptor).addPathPatterns("/*Action/**").excludePathPatterns("/userAction/login"); + registry.addInterceptor(loginCheckInterceptor) + .addPathPatterns("/*Action/**") + .excludePathPatterns("/userAction/login", "/userAction/logout"); + } + + @Override + public void addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry registry) { + // 仅在开发环境启用跨域 + String[] activeProfiles = environment.getActiveProfiles(); + boolean isDev = false; + + if (activeProfiles.length == 0) { + // 如果没有指定profile,默认使用dev + isDev = true; + } else { + for (String profile : activeProfiles) { + if ("dev".equals(profile) || "development".equals(profile) || "test".equals(profile)) { + isDev = true; + break; + } + } + } + + if (isDev) { + registry.addMapping("/**") // 允许所有路径 + .allowedOriginPatterns("*") // 允许所有来源 + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") // 允许的HTTP方法 + .allowedHeaders("*") // 允许所有请求头 + .allowCredentials(true) // 允许发送cookie + .maxAge(3600); // 预检请求的有效期 + } } } diff --git a/test05/src/main/java/test05/controller/CourseManagementController.java b/test05/src/main/java/test05/controller/CourseManagementController.java new file mode 100644 index 0000000..af1fa24 --- /dev/null +++ b/test05/src/main/java/test05/controller/CourseManagementController.java @@ -0,0 +1,63 @@ +package test05.controller; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import test05.pojo.Result; + +/** + * 课程管理控制器 - 提供示例API + */ +@RestController +@RequestMapping("/courseAction") +public class CourseManagementController { + + /** + * 选课API - 学生端API示例 + * 注意:这是空实现,因为还没有选课表 + */ + @RequestMapping("/selectCourse") + public Result selectCourse(Integer courseId, HttpServletRequest request) { + // TODO: 实现选课功能 + // 需要创建选课表和相关的映射关系表 + return Result.success("选课功能待实现 - 课程ID: " + courseId); + } + + /** + * 创建课程API - 老师端API示例 + * 注意:这是空实现,因为还没有课程表和老师用户映射关系表 + */ + @RequestMapping("/createCourse") + public Result createCourse(String courseName, String courseDescription, HttpServletRequest request) { + // TODO: 实现创建课程功能 + // 需要创建课程表和课程与老师的映射关系表 + return Result.success("创建课程功能待实现 - 课程名称: " + courseName); + } + + /** + * 获取课程列表API - 公共API示例 + */ + @RequestMapping("/getCourses") + public Result getCourses(Integer page, Integer pageSize) { + // TODO: 实现获取课程列表功能 + return Result.success("获取课程列表功能待实现"); + } + + /** + * 删除课程API - 老师端API示例 + */ + @RequestMapping("/deleteCourse") + public Result deleteCourse(Integer courseId, HttpServletRequest request) { + // TODO: 实现删除课程功能 + return Result.success("删除课程功能待实现 - 课程ID: " + courseId); + } + + /** + * 更新课程API - 老师端API示例 + */ + @RequestMapping("/updateCourse") + public Result updateCourse(Integer courseId, String courseName, String courseDescription, HttpServletRequest request) { + // TODO: 实现更新课程功能 + return Result.success("更新课程功能待实现 - 课程ID: " + courseId); + } +} \ No newline at end of file diff --git a/test05/src/main/java/test05/controller/TeacherController.java b/test05/src/main/java/test05/controller/TeacherController.java new file mode 100644 index 0000000..7a1a118 --- /dev/null +++ b/test05/src/main/java/test05/controller/TeacherController.java @@ -0,0 +1,56 @@ +package test05.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import test05.pojo.Result; +import test05.pojo.Teacher; +import test05.service.TeacherService; + +@RestController +@RequestMapping("/teacherAction") +public class TeacherController { + private final TeacherService teacherService; + @Autowired + public TeacherController(TeacherService teacherService) { + this.teacherService = teacherService; + } + + @RequestMapping("/get") + public Result get(Teacher teacher, Integer page, Integer pageSize) { + if (pageSize == null) { + pageSize = 10; + } + if (page == null) { + page = 1; + } + int offset = Math.max(0, (page - 1)) * pageSize; + return teacherService.getTeacher(teacher, offset, pageSize); + } + + @RequestMapping("/add") + public Result add(Teacher teacher) { + if (teacher == null || teacher.getId() != -1) { + return Result.error("bad request"); + }else{ + return teacherService.addTeacher(teacher); + } + } + + @RequestMapping("/edit") + public Result edit(Teacher teacher) { + if (teacher == null || teacher.getId() == -1) { + return Result.error("bad request"); + } + return teacherService.updateTeacher(teacher); + } + + @RequestMapping("/delete") + public Result delete(Teacher teacher) { + if (teacher == null || teacher.getId() <= 0) { + return Result.error("bad request"); + } + return teacherService.deleteTeacher(teacher); + } +} diff --git a/test05/src/main/java/test05/controller/UserManagementController.java b/test05/src/main/java/test05/controller/UserManagementController.java new file mode 100644 index 0000000..b271092 --- /dev/null +++ b/test05/src/main/java/test05/controller/UserManagementController.java @@ -0,0 +1,106 @@ +package test05.controller; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import test05.enums.UserRole; +import test05.pojo.Result; +import test05.pojo.User; +import test05.service.UserService; +import test05.utils.UserRoleUtils; + +/** + * 用户管理控制器 - 处理用户创建功能 + */ +@RestController +@RequestMapping("/userManagementAction") +public class UserManagementController { + private final UserService userService; + + @Autowired + public UserManagementController(UserService userService) { + this.userService = userService; + } + + /** + * 创建老师用户 - 仅管理员可访问 + */ + @RequestMapping("/createTeacher") + public Result createTeacher(String username, String password, HttpServletRequest request) { + String token = request.getHeader("token"); + UserRole creatorRole = UserRoleUtils.getUserRoleFromToken(token); + + // 检查权限 + if (!UserRoleUtils.canCreateUser(creatorRole, UserRole.TEACHER)) { + return Result.error("access denied"); + } + + // 验证用户名格式 + if (!username.startsWith(UserRole.TEACHER.getPrefix())) { + return Result.error("teacher username must start with 't_'"); + } + + if (!UserRole.isValidUsername(username)) { + return Result.error("invalid username format"); + } + + // 创建用户 + User newUser = new User(username, password); + return userService.addUser(newUser); + } + + /** + * 创建学生用户 - 管理员和老师可访问 + */ + @RequestMapping("/createStudent") + public Result createStudent(String username, String password, HttpServletRequest request) { + String token = request.getHeader("token"); + UserRole creatorRole = UserRoleUtils.getUserRoleFromToken(token); + + // 检查权限 + if (!UserRoleUtils.canCreateUser(creatorRole, UserRole.STUDENT)) { + return Result.error("access denied"); + } + + // 验证用户名格式 + if (!username.startsWith(UserRole.STUDENT.getPrefix())) { + return Result.error("student username must start with 's_'"); + } + + if (!UserRole.isValidUsername(username)) { + return Result.error("invalid username format"); + } + + // 创建用户 + User newUser = new User(username, password); + return userService.addUser(newUser); + } + + /** + * 创建管理员用户 - 仅管理员可访问 + */ + @RequestMapping("/createAdmin") + public Result createAdmin(String username, String password, HttpServletRequest request) { + String token = request.getHeader("token"); + UserRole creatorRole = UserRoleUtils.getUserRoleFromToken(token); + + // 只有管理员可以创建管理员 + if (creatorRole != UserRole.ADMIN) { + return Result.error("access denied"); + } + + // 验证用户名格式 + if (!username.startsWith(UserRole.ADMIN.getPrefix())) { + return Result.error("admin username must start with 'a_'"); + } + + if (!UserRole.isValidUsername(username)) { + return Result.error("invalid username format"); + } + + // 创建用户 + User newUser = new User(username, password); + return userService.addUser(newUser); + } +} \ No newline at end of file diff --git a/test05/src/main/java/test05/enums/UserRole.java b/test05/src/main/java/test05/enums/UserRole.java new file mode 100644 index 0000000..11794c9 --- /dev/null +++ b/test05/src/main/java/test05/enums/UserRole.java @@ -0,0 +1,73 @@ +package test05.enums; + +/** + * 用户角色枚举 + */ +public enum UserRole { + ADMIN("a_", "管理员"), + TEACHER("t_", "老师"), + STUDENT("s_", "学生"); + + private final String prefix; + private final String description; + + UserRole(String prefix, String description) { + this.prefix = prefix; + this.description = description; + } + + public String getPrefix() { + return prefix; + } + + public String getDescription() { + return description; + } + + /** + * 根据用户名获取角色 + */ + public static UserRole getRoleByUsername(String username) { + if (username == null || username.isEmpty()) { + return null; + } + + if (username.startsWith(ADMIN.getPrefix())) { + return ADMIN; + } else if (username.startsWith(TEACHER.getPrefix())) { + return TEACHER; + } else if (username.startsWith(STUDENT.getPrefix())) { + return STUDENT; + } + + return null; + } + + /** + * 检查用户名格式是否有效 + */ + public static boolean isValidUsername(String username) { + return getRoleByUsername(username) != null; + } + + /** + * 检查角色是否有权限访问指定路径 + */ + public boolean hasAccessToPath(String path) { + if (path == null) return false; + + switch (this) { + case ADMIN: + // 管理员可以访问所有API + return true; + case TEACHER: + // 老师可以访问老师端API + return path.contains("/teacherAction/") || path.contains("/studentAction/"); + case STUDENT: + // 学生只能访问学生端API + return path.contains("/studentAction/"); + default: + return false; + } + } +} \ No newline at end of file diff --git a/test05/src/main/java/test05/interceptor/LoginCheckInterceptor.java b/test05/src/main/java/test05/interceptor/LoginCheckInterceptor.java index 9635433..a3f082d 100644 --- a/test05/src/main/java/test05/interceptor/LoginCheckInterceptor.java +++ b/test05/src/main/java/test05/interceptor/LoginCheckInterceptor.java @@ -3,20 +3,35 @@ package test05.interceptor; import com.alibaba.fastjson2.JSONObject; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; +import test05.enums.UserRole; import test05.pojo.Result; import test05.utils.JwtUtils; +import test05.utils.UserRoleUtils; @Component public class LoginCheckInterceptor implements HandlerInterceptor { @Override - public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception { + public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception { String token = request.getHeader("token"); try{ JwtUtils.parseJwt(token); + + // 检查角色权限 + UserRole userRole = UserRoleUtils.getUserRoleFromToken(token); + if (userRole == null) { + response.getWriter().print(JSONObject.toJSONString(Result.error("invalid user role"))); + return false; + } + + String requestURI = request.getRequestURI(); + if (!userRole.hasAccessToPath(requestURI)) { + response.getWriter().print(JSONObject.toJSONString(Result.error("access denied"))); + return false; + } + }catch(Exception e){ response.getWriter().print(JSONObject.toJSONString(Result.error("not login"))); return false; @@ -24,11 +39,11 @@ public class LoginCheckInterceptor implements HandlerInterceptor { return true; } @Override - public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) { + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { } @Override - public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) { + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { } } diff --git a/test05/src/main/java/test05/mapper/TeacherMapper.java b/test05/src/main/java/test05/mapper/TeacherMapper.java index 136bedb..83636d3 100644 --- a/test05/src/main/java/test05/mapper/TeacherMapper.java +++ b/test05/src/main/java/test05/mapper/TeacherMapper.java @@ -7,7 +7,8 @@ import java.util.List; @Mapper public interface TeacherMapper { - List selectTeacher(Teacher teacher); + List selectTeacher(Teacher teacher, int offset, int pageSize); + int selectTeacherCount(Teacher teacher); int insertTeacher(Teacher teacher); int updateTeacher(Teacher teacher); int deleteTeacher(Teacher teacher); diff --git a/test05/src/main/java/test05/pojo/Teacher.java b/test05/src/main/java/test05/pojo/Teacher.java index d6e8058..72fee46 100644 --- a/test05/src/main/java/test05/pojo/Teacher.java +++ b/test05/src/main/java/test05/pojo/Teacher.java @@ -10,18 +10,24 @@ import lombok.NonNull; @NoArgsConstructor public class Teacher { @NonNull - Integer N_TEACHER_ID=-1; + // Integer N_TEACHER_ID=-1; + Integer id = -1; @NonNull - String VC_TEACHER_NAME=""; + // String VC_TEACHER_NAME=""; + String name = ""; @NonNull - Integer N_SEX=-1; + // Integer N_SEX=-1; + Integer sex = -1; @NonNull - String VC_BIRTH=""; + // String VC_BIRTH=""; + String birth = ""; @NonNull - Integer N_EDUC=-1; + // Integer N_EDUC=-1; + Integer educ = -1; @NonNull - Integer N_TITLE=-1; + // Integer N_TITLE=-1; + Integer title = -1; public Teacher(int id){ - N_TEACHER_ID = id; + this.id = id; } } diff --git a/test05/src/main/java/test05/service/TeacherService.java b/test05/src/main/java/test05/service/TeacherService.java new file mode 100644 index 0000000..2bc8da6 --- /dev/null +++ b/test05/src/main/java/test05/service/TeacherService.java @@ -0,0 +1,11 @@ +package test05.service; + +import test05.pojo.Result; +import test05.pojo.Teacher; + +public interface TeacherService { + Result getTeacher(Teacher teacher, int page, int pageSize); + Result addTeacher(Teacher teacher); + Result updateTeacher(Teacher teacher); + Result deleteTeacher(Teacher teacher); +} diff --git a/test05/src/main/java/test05/service/UserService.java b/test05/src/main/java/test05/service/UserService.java index 256979b..9fb412b 100644 --- a/test05/src/main/java/test05/service/UserService.java +++ b/test05/src/main/java/test05/service/UserService.java @@ -10,4 +10,11 @@ public interface UserService { * @return 登录结果 */ Result login(User user); + + /** + * 添加用户服务 + * @param user 要添加的用户 + * @return 添加结果 + */ + Result addUser(User user); } diff --git a/test05/src/main/java/test05/service/impl/TeacherServiceImpl.java b/test05/src/main/java/test05/service/impl/TeacherServiceImpl.java new file mode 100644 index 0000000..a2d3b90 --- /dev/null +++ b/test05/src/main/java/test05/service/impl/TeacherServiceImpl.java @@ -0,0 +1,53 @@ +package test05.service.impl; + +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import test05.mapper.TeacherMapper; +import test05.pojo.Result; +import test05.pojo.Teacher; +import test05.service.TeacherService; + +@Service +public class TeacherServiceImpl implements TeacherService{ + private final TeacherMapper teacherMapper; + + @Autowired + public TeacherServiceImpl(TeacherMapper teacherMapper) { + this.teacherMapper = teacherMapper; + } + + @Override + public Result getTeacher(Teacher teacher, int page, int pageSize) { + try{ + int offset = Math.max(0, (page - 1)) * pageSize; + List list = teacherMapper.selectTeacher(teacher, offset, pageSize); + int total = teacherMapper.selectTeacherCount(teacher); + return Result.success(list, total); + }catch (Exception e){ + return Result.error(e.getMessage()); + } + } + + @Override + public Result addTeacher(Teacher teacher) { + // TODO:校验数据 + int count = teacherMapper.insertTeacher(teacher); + return Result.success(count>0); + } + + @Override + public Result updateTeacher(Teacher teacher) { + int count = teacherMapper.updateTeacher(teacher); + return Result.success(count>0); + } + + @Override + public Result deleteTeacher(Teacher teacher) { + int count = teacherMapper.deleteTeacher(teacher); + return Result.success(count>0); + } + +} diff --git a/test05/src/main/java/test05/service/impl/UserServiceImpl.java b/test05/src/main/java/test05/service/impl/UserServiceImpl.java index 5cc5b16..d2d2287 100644 --- a/test05/src/main/java/test05/service/impl/UserServiceImpl.java +++ b/test05/src/main/java/test05/service/impl/UserServiceImpl.java @@ -34,4 +34,21 @@ public class UserServiceImpl implements UserService { return Result.error("password incorrect"); } } + + @Override + public Result addUser(User user) { + // 检查用户名是否已存在 + User existingUser = userMapper.selectUserByName(user.getName()); + if (existingUser != null) { + return Result.error("username already exists"); + } + + // 插入新用户 + int result = userMapper.insertUser(user); + if (result > 0) { + return Result.success("user created successfully"); + } else { + return Result.error("failed to create user"); + } + } } diff --git a/test05/src/main/java/test05/utils/UserRoleUtils.java b/test05/src/main/java/test05/utils/UserRoleUtils.java new file mode 100644 index 0000000..ff94cb6 --- /dev/null +++ b/test05/src/main/java/test05/utils/UserRoleUtils.java @@ -0,0 +1,113 @@ +package test05.utils; + +import java.util.Map; + +import com.alibaba.fastjson2.JSONObject; + +import test05.enums.UserRole; + +/** + * 用户角色工具类 + */ +public class UserRoleUtils { + + /** + * 从JWT token中提取用户名并获取角色 + */ + public static UserRole getUserRoleFromToken(String token) { + try { + if (token == null || token.isEmpty()) { + return null; + } + + // 解析JWT token获取用户信息 + Map claims_map = test05.utils.JwtUtils.parseJwt(token); + com.alibaba.fastjson2.JSONObject claims = new JSONObject(claims_map) ; + if (claims == null || !claims.containsKey("data")) { + return null; + } + + Object dataObj = claims.get("data"); + if (dataObj == null) { + return null; + } + + com.alibaba.fastjson2.JSONObject data; + if (dataObj instanceof com.alibaba.fastjson2.JSONObject) { + data = (com.alibaba.fastjson2.JSONObject) dataObj; + } else { + data = com.alibaba.fastjson2.JSONObject.parseObject(dataObj.toString()); + } + + if (!data.containsKey("name")) { + return null; + } + + String username = data.getString("name"); + return UserRole.getRoleByUsername(username); + + } catch (Exception e) { + return null; + } + } + + /** + * 从JWT token中提取用户名 + */ + public static String getUsernameFromToken(String token) { + try { + if (token == null || token.isEmpty()) { + return null; + } + + com.alibaba.fastjson2.JSONObject claims = new JSONObject(test05.utils.JwtUtils.parseJwt(token)); + if (claims == null || !claims.containsKey("data")) { + return null; + } + + Object dataObj = claims.get("data"); + if (dataObj == null) { + return null; + } + + com.alibaba.fastjson2.JSONObject data; + if (dataObj instanceof com.alibaba.fastjson2.JSONObject) { + data = (com.alibaba.fastjson2.JSONObject) dataObj; + } else { + data = com.alibaba.fastjson2.JSONObject.parseObject(dataObj.toString()); + } + + if (!data.containsKey("name")) { + return null; + } + + return data.getString("name"); + + } catch (Exception e) { + return null; + } + } + + /** + * 检查用户是否有权限创建指定角色的用户 + */ + public static boolean canCreateUser(UserRole creatorRole, UserRole targetRole) { + if (creatorRole == null || targetRole == null) { + return false; + } + + switch (creatorRole) { + case ADMIN: + // 管理员可以创建老师和用户 + return targetRole == UserRole.TEACHER || targetRole == UserRole.STUDENT; + case TEACHER: + // 老师可以创建学生 + return targetRole == UserRole.STUDENT; + case STUDENT: + // 学生不能创建用户 + return false; + default: + return false; + } + } +} \ No newline at end of file diff --git a/test05/src/main/resources/application-dev.properties b/test05/src/main/resources/application-dev.properties new file mode 100644 index 0000000..2ad4be7 --- /dev/null +++ b/test05/src/main/resources/application-dev.properties @@ -0,0 +1,16 @@ +# 开发环境配置 + +# 开发环境数据库配置 +spring.datasource.url=jdbc:mysql://www.awin-x.top:63306/db_student +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.username=admin +spring.datasource.password=3131313xY + +spring.datasource.druid.validation-query=SELECT 1 + +mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl + +# 开发环境特定配置 +logging.level.root=INFO +logging.level.test05=DEBUG +spring.devtools.restart.enabled=true \ No newline at end of file diff --git a/test05/src/main/resources/application-prod.properties b/test05/src/main/resources/application-prod.properties new file mode 100644 index 0000000..6fca755 --- /dev/null +++ b/test05/src/main/resources/application-prod.properties @@ -0,0 +1,16 @@ +# 生产环境配置 + +# 生产环境数据库配置(请根据实际情况修改) +spring.datasource.url=jdbc:mysql://your-prod-db-host:3306/db_student +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver +spring.datasource.username=prod_user +spring.datasource.password=prod_password + +spring.datasource.druid.validation-query=SELECT 1 + +# 生产环境关闭SQL日志输出 +mybatis.configuration.log-impl=org.apache.ibatis.logging.nologging.NoLoggingImpl + +# 生产环境特定配置 +logging.level.root=WARN +spring.devtools.restart.enabled=false \ No newline at end of file diff --git a/test05/src/main/resources/application.properties b/test05/src/main/resources/application.properties index b5f8ffa..08e6a4f 100644 --- a/test05/src/main/resources/application.properties +++ b/test05/src/main/resources/application.properties @@ -1,15 +1,7 @@ spring.application.name=test05 -#\u4F7F\u7528\u672C\u5730\u6D4B\u8BD5\u6570\u636E\u5E93 -#spring.datasource.driver-class-name=org.sqlite.JDBC -spring.datasource.url=jdbc:mysql://www.awin-x.top:63306/db_student -#spring.datasource.url=jdbc:sqlite::resource:db/db_student.db -##\u4F7F\u7528mysql\u6570\u636E\u5E93 -spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -#spring.datasource.url=jdbc:mysql://localhost:3306/db_student.db -spring.datasource.username=admin -spring.datasource.password=3131313xY +# 默认激活开发环境profile +spring.profiles.active=dev +# 公共配置(可被各环境覆盖) spring.datasource.druid.validation-query=SELECT 1 - -mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl diff --git a/test05/src/main/resources/test05/mapper/StudentMapper.xml b/test05/src/main/resources/test05/mapper/StudentMapper.xml index 4c56441..1352c9a 100644 --- a/test05/src/main/resources/test05/mapper/StudentMapper.xml +++ b/test05/src/main/resources/test05/mapper/StudentMapper.xml @@ -22,7 +22,7 @@ AND (VC_DETAIL LIKE CONCAT('%', #{student.detail}, '%') OR (#{student.detail} = '' AND VC_DETAIL IS NULL)) ORDER BY N_STUDENT_ID DESC - LIMIT #{offset},#{pageSize} + LIMIT #{offset},#{pageSize}; - SELECT * + SELECT N_TEACHER_ID AS id, + VC_TEACHER_NAME AS name, + N_SEX AS sex, + VC_BIRTH AS birth, + N_EDUC AS educ, + N_TITLE AS title FROM t_teacher - WHERE #{N_TEACHER_ID} IN (N_TEACHER_ID, -1) - AND VC_TEACHER_NAME LIKE CONCAT('%', #{VC_TEACHER_NAME}, '%') - AND #{N_SEX} IN (N_SEX, -1) - AND VC_BIRTH LIKE CONCAT('%', #{VC_BIRTH}, '%') - AND #{N_EDUC} IN (N_EDUC, -1) - AND #{N_TITLE} IN (N_TITLE, -1) + WHERE #{teacher.id} in (N_TEACHER_ID, -1) + AND VC_TEACHER_NAME LIKE CONCAT('%', #{teacher.name}, '%') + AND #{teacher.sex} in (N_SEX, -1) + AND #{teacher.educ} in (N_EDUC, -1) + AND #{teacher.title} in (N_TITLE, -1) + ORDER BY N_TEACHER_ID DESC + LIMIT #{offset},#{pageSize}; + + INSERT INTO t_teacher (VC_TEACHER_NAME, N_SEX, VC_BIRTH, N_EDUC, N_TITLE) - VALUES (#{VC_TEACHER_NAME},#{N_SEX},#{VC_BIRTH},#{N_EDUC},#{N_TITLE}) + VALUES (#{name},#{sex},#{birth},#{educ},#{title}); UPDATE t_teacher - SET VC_TEACHER_NAME=#{VC_TEACHER_NAME}, - N_SEX=#{N_SEX}, - VC_BIRTH=#{VC_BIRTH}, - N_EDUC=#{N_EDUC}, - N_TITLE=#{N_TITLE} - WHERE N_TEACHER_ID = #{N_TEACHER_ID} + SET VC_TEACHER_NAME=#{name}, + N_SEX=#{sex}, + VC_BIRTH=#{birth}, + N_EDUC=#{educ}, + N_TITLE=#{title} + WHERE N_TEACHER_ID=#{N_TEACHER_ID}; DELETE FROM t_teacher - WHERE N_TEACHER_ID = #{N_TEACHER_ID} + WHERE N_TEACHER_ID = #{id}; \ No newline at end of file diff --git a/test05/src/test/java/test05/Test05ApplicationTests.java b/test05/src/test/java/test05/Test05ApplicationTests.java index be7d1e8..65333b7 100644 --- a/test05/src/test/java/test05/Test05ApplicationTests.java +++ b/test05/src/test/java/test05/Test05ApplicationTests.java @@ -104,40 +104,40 @@ class Test05ApplicationTests { @Test public void teacherMapperTest() { Teacher teacher = new Teacher(); - teacher.setVC_TEACHER_NAME("测试老师"); - teacher.setN_EDUC(2); - teacher.setN_TITLE(2); - teacher.setVC_BIRTH("20230810"); - teacher.setN_SEX(1); + teacher.setName("测试老师"); + teacher.setEduc(2); + teacher.setTitle(2); + teacher.setBirth("20230810"); + teacher.setSex(1); //添加老师 teacherMapper.insertTeacher(teacher); - System.out.println(teacher.getN_TEACHER_ID()); - if (teacher.getN_TEACHER_ID() == -1) { + System.out.println(teacher.getId()); + if (teacher.getId() == -1) { System.out.println("老师信息添加错误"); throw new TestAbortedException(); } //查询老师 - List selectResult = teacherMapper.selectTeacher(new Teacher()); + List selectResult = teacherMapper.selectTeacher(new Teacher(), 0, 10); if (selectResult.isEmpty()) { System.out.println("查询老师信息出现错误"); throw new TestAbortedException(); } System.out.println(selectResult); - teacher.setN_SEX(0); + teacher.setSex(0); //修改老师 teacherMapper.updateTeacher(teacher); - selectResult = teacherMapper.selectTeacher(teacher); + selectResult = teacherMapper.selectTeacher(teacher, 0, 10); if (selectResult.size() != 1) { System.out.println("查询老师信息出现错误"); throw new TestAbortedException(); } - if (selectResult.getFirst().getN_SEX() != 0) { + if (selectResult.getFirst().getSex() != 0) { System.out.println("老师信息修改出现错误"); throw new TestAbortedException(); } //删除老师 teacherMapper.deleteTeacher(teacher); - selectResult = teacherMapper.selectTeacher(teacher); + selectResult = teacherMapper.selectTeacher(teacher, 0, 10); if (!selectResult.isEmpty()) { System.out.println("老师信息删除出现错误"); } diff --git a/test05/test05.svg b/test05/test05.svg new file mode 100644 index 0000000..8a662ac --- /dev/null +++ b/test05/test05.svg @@ -0,0 +1 @@ +test05aopconfigcontrollerexceptioninterceptormapperpojoserviceimplutilsTest05Application+main(String args): void#configure(SpringApplicationBuilder builder): SpringApplicationBuilderTest05ApplicationTests~courseMapper: CourseMapper~scoreMapper: ScoreMapper~studentMapper: StudentMapper~teacherMapper: TeacherMapper~userMapper: UserMapper+courseMapperTest(): void+studentMapperTest(): void+teacherMapperTest(): void+userMapperTest(): void+genJwtTest(): voidTimeAspect+recordTime(ProceedingJoinPoint joinPoint): ObjectDatabaseConfig~ds: DruidDataSource~input: Boolean+init(): voidWebConfig-loginCheckInterceptor: LoginCheckInterceptor+addInterceptors(InterceptorRegistry registry): voidCourseController-courseService: CourseService+getCourse(String keyword, Integer type, Integer grade, Float credit, Integer page, Integer pageSize): Result+editCourse(Course course): Result+addCourse(Course course): Result+deleteCourse(Integer id): ResultHello+hello(): StringLoginController-userService: UserService+login(String username, String password, HttpSession session): Result+logout(HttpSession session): ResultScoreController~scoreService: ScoreService+getScore(int studentId, int courseId): Result+updateScore(Score score): Result+addScore(Student student, Course course, Float score): Result+deleteScore(Score score): Result+getScoreByStu(String studentCode): ResultstudentController-studentService: StudentService+get(Student student, Integer page, Integer pageSize): Result+add(Student student): Result+edit(Student student): Result+delete(Student student): ResultTeacherController-teacherService: TeacherService+get(Teacher teacher, Integer page, Integer pageSize): Result+add(Teacher teacher): Result+edit(Teacher teacher): Result+delete(Teacher teacher): ResultGlobalExceptionHandler+ex(Exception e): ResultLoginCheckInterceptor+preHandle(HttpServletRequest request, HttpServletResponse response, Object handler): boolean+postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView): void+afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex): voidCourseMapper~selectCourse(Course course, int offset, int pageSize): List~insertCourse(Course course): int~updateCourse(Course course): int~deleteCourse(Course course): int~selectCourseCount(Course course): intScoreMapper~selectScore(Score score): List~selectScoreCount(Score score): int~insertScore(Score score): int~deleteScore(Score score): int~updateScore(Score score): intStudentMapper~selectStudent(Student student, int offset, int pageSize): List~selectStudentCount(Student student): int~insertStudent(Student student): int~updateStudent(Student student): int~deleteStudent(Student student): intTeacherMapper~selectTeacher(Teacher teacher, int offset, int pageSize): List~selectTeacherCount(Teacher teacher): int~insertTeacher(Teacher teacher): int~updateTeacher(Teacher teacher): int~deleteTeacher(Teacher teacher): intUserMapper~selectUser(User user, int offset, int pageSize): List~selectUserByName(String username): User~insertUser(User user): int~deleteUser(User user): int~updateUser(User user): intCourse-id: Integer-name: String-type: Integer-credit: Float-grade: Integer-major: Integer-detail: String+isComplete(): booleanResult-success: boolean-count: int-msg: String-data: Object+success(Object data, String msg): Result+success(Object data, int count): Result+success(Object data, String msg, int count): Result+success(Object data): Result+success(): Result+success(boolean isSuccess): Result+error(String msg): Result+error(String msg, Object data): ResultScore-studentId: Integer-courseId: Integer-score: FloatStudent-id: Integer-code: String-name: String-sex: Integer-grade: Integer-major: Integer-detail: StringTeacher~id: Integer~name: String~sex: Integer~birth: String~educ: Integer~title: IntegerUser-id: Integer-name: String-password: StringCourseService~addCourse(Course course): Result~deleteCourse(Course course): Result~updateCourse(Course course): Result~getCourse(Course course, int page, int pageSize): ResultScoreService~addScore(Student student, Course course, Float score): Result~getScore(Student student, Course course): Result~deleteScore(Score score): Result~updateScore(Score score): Result~getStudentScore(Student student): ResultStudentService~getStudent(Student student, int page, int pageSize): Result~addStudent(Student student): Result~updateStudent(Student student): Result~deleteStudent(Student student): ResultUserService~login(User user): ResultTeacherService~getTeacher(Teacher teacher, int page, int pageSize): Result~addTeacher(Teacher teacher): Result~updateTeacher(Teacher teacher): Result~deleteTeacher(Teacher teacher): ResultCourseServiceImpl-courseMapper: CourseMapper-courseCheck(Course course): String+addCourse(Course course): Result+deleteCourse(Course course): Result+updateCourse(Course course): Result+getCourse(Course course, int page, int pageSize): ResultScoreServiceImpl~scoreMapper: ScoreMapper~studentMapper: StudentMapper~courseMapper: CourseMapper+addScore(Student student, Course course, Float score): Result+getScore(Student student, Course course): Result+deleteScore(Score score): Result+updateScore(Score score): Result+getStudentScore(Student student): ResultStudentServiceImpl-studentMapper: StudentMapper+getStudent(Student student, int page, int pageSize): Result+addStudent(Student student): Result+updateStudent(Student student): Result+deleteStudent(Student student): ResultUserServiceImpl-userMapper: UserMapper+setUserMapper(UserMapper userMapper): void+login(User user): ResultTeacherServiceImpl-teacherMapper: TeacherMapper+getTeacher(Teacher teacher, int page, int pageSize): Result+addTeacher(Teacher teacher): Result+updateTeacher(Teacher teacher): Result+deleteTeacher(Teacher teacher): ResultJwtUtils-KEY: SecretKey-expireTime: int+getJwt(Map map): String+setExpireTime(int ms, int seconds, int minutes, int hour, int day): void+getExpireTime(): Date+parseJwt(String jwt): Map \ No newline at end of file