From ddf078bae5c361b5ccadd09cc14628ec2700a294 Mon Sep 17 00:00:00 2001 From: poppoppuppylove <431792974@qq.com> Date: Sun, 6 Oct 2024 03:37:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E4=BF=AE=E6=94=B9=E5=AD=A6=E7=94=9F?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=EF=BC=8C=E5=AE=8C=E5=96=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 146 ++++++++++++++++++ .../attendance/config/SecurityConfig.java | 33 ++++ .../controller/RollCallController.java | 24 +++ .../controller/StudentController.java | 76 ++++++--- .../controller/TeacherController.java | 34 ++++ .../attendance/entity/PointsRequest.java | 15 ++ .../RollCallResponse.java} | 15 +- .../attendance/entity/RollCallSettings.java | 15 ++ .../example/attendance/entity/Student.java | 53 +++++-- .../StudentDTO.java => entity/Teacher.java} | 13 +- .../listener/StudentExcelListener.java | 29 ---- .../attendance/mapper/StudentMapper.java | 43 +++++- .../attendance/mapper/TeacherMapper.java | 15 ++ .../attendance/service/RollCallService.java | 10 ++ .../attendance/service/StudentService.java | 17 +- .../attendance/service/TeacherService.java | 23 +++ .../service/impl/RollCallServiceImpl.java | 110 +++++++++++++ .../service/impl/StudentServiceImpl.java | 100 +++++++++--- .../service/impl/TeacherServiceImpl.java | 37 +++++ .../com/example/attendance/util/JWTUtil.java | 43 ++++++ src/main/resources/application.properties | 2 +- .../example/attendance/PasswordEncryptor.java | 24 +++ 22 files changed, 768 insertions(+), 109 deletions(-) create mode 100644 pom.xml create mode 100644 src/main/java/com/example/attendance/config/SecurityConfig.java create mode 100644 src/main/java/com/example/attendance/controller/RollCallController.java create mode 100644 src/main/java/com/example/attendance/controller/TeacherController.java create mode 100644 src/main/java/com/example/attendance/entity/PointsRequest.java rename src/main/java/com/example/attendance/{dto/StudentExcelDTO.java => entity/RollCallResponse.java} (51%) create mode 100644 src/main/java/com/example/attendance/entity/RollCallSettings.java rename src/main/java/com/example/attendance/{dto/StudentDTO.java => entity/Teacher.java} (56%) delete mode 100644 src/main/java/com/example/attendance/listener/StudentExcelListener.java create mode 100644 src/main/java/com/example/attendance/mapper/TeacherMapper.java create mode 100644 src/main/java/com/example/attendance/service/RollCallService.java create mode 100644 src/main/java/com/example/attendance/service/TeacherService.java create mode 100644 src/main/java/com/example/attendance/service/impl/RollCallServiceImpl.java create mode 100644 src/main/java/com/example/attendance/service/impl/TeacherServiceImpl.java create mode 100644 src/main/java/com/example/attendance/util/JWTUtil.java create mode 100644 src/test/java/com/example/attendance/PasswordEncryptor.java diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..afa90ec --- /dev/null +++ b/pom.xml @@ -0,0 +1,146 @@ + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.3 + + + com.example + attendance + 0.0.1-SNAPSHOT + attendance + attendance + + + 17 + + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 3.0.3 + + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + + com.mysql + mysql-connector-j + runtime + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter-test + 3.0.3 + test + + + + + com.alibaba + easyexcel + 3.0.5 + + + + + org.springframework.boot + spring-boot-starter-security + + + + + io.jsonwebtoken + jjwt-api + 0.11.5 + + + + io.jsonwebtoken + jjwt-impl + 0.11.5 + runtime + + + + io.jsonwebtoken + jjwt-jackson + 0.11.5 + runtime + + + + + org.springframework.security + spring-security-crypto + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 17 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + diff --git a/src/main/java/com/example/attendance/config/SecurityConfig.java b/src/main/java/com/example/attendance/config/SecurityConfig.java new file mode 100644 index 0000000..a4a6c48 --- /dev/null +++ b/src/main/java/com/example/attendance/config/SecurityConfig.java @@ -0,0 +1,33 @@ +package com.example.attendance.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; + +@Configuration +@EnableWebSecurity +public class SecurityConfig { + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(csrf -> csrf.disable()) // 关闭 CSRF 保护(适用于测试,生产环境中请谨慎处理) + .authorizeHttpRequests(authz -> authz + .requestMatchers("/api/teacher/register", "/api/teacher/login").permitAll() // 允许注册和登录接口匿名访问 + .anyRequest().authenticated() // 其他请求需要认证 + ) + .formLogin(form -> form.disable()) // 关闭表单登录(如有需要可以调整) + .httpBasic(httpBasic -> httpBasic.disable()); // 关闭基本认证(如有需要可以调整) + + return http.build(); + } + + +} diff --git a/src/main/java/com/example/attendance/controller/RollCallController.java b/src/main/java/com/example/attendance/controller/RollCallController.java new file mode 100644 index 0000000..701fb19 --- /dev/null +++ b/src/main/java/com/example/attendance/controller/RollCallController.java @@ -0,0 +1,24 @@ +package com.example.attendance.controller; + +import com.example.attendance.entity.RollCallResponse; +import com.example.attendance.entity.RollCallSettings; +import com.example.attendance.entity.Student; +import com.example.attendance.service.RollCallService; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +@RestController +@RequestMapping("/api/rollcall") +public class RollCallController { + + private final RollCallService rollCallService; + + public RollCallController(RollCallService rollCallService) { + this.rollCallService = rollCallService; + } + + @PostMapping("/start") + public RollCallResponse startRollCall(@RequestBody List students, @RequestBody RollCallSettings settings) { + return rollCallService.startRollCall(students, settings); + } +} diff --git a/src/main/java/com/example/attendance/controller/StudentController.java b/src/main/java/com/example/attendance/controller/StudentController.java index 54f4aa0..715a706 100644 --- a/src/main/java/com/example/attendance/controller/StudentController.java +++ b/src/main/java/com/example/attendance/controller/StudentController.java @@ -1,5 +1,6 @@ package com.example.attendance.controller; +import com.example.attendance.entity.PointsRequest; import com.example.attendance.entity.Student; import com.example.attendance.service.StudentService; import org.springframework.beans.factory.annotation.Autowired; @@ -16,35 +17,71 @@ public class StudentController { @Autowired private StudentService studentService; - @GetMapping("/") + @GetMapping("/{id}") + public ResponseEntity getStudentById(@PathVariable Long id) { + Student student = studentService.findById(id); + return ResponseEntity.ok(student); + } + + @GetMapping("/studentNumber/{studentNumber}") + public ResponseEntity getStudentByStudentNumber(@PathVariable String studentNumber) { + Student student = studentService.findByStudentNumber(studentNumber); + return ResponseEntity.ok(student); + } + + @GetMapping public ResponseEntity> getAllStudents() { - List students = studentService.getAllStudents(); + List students = studentService.findAll(); return ResponseEntity.ok(students); } - @GetMapping("/{id}") - public ResponseEntity getStudent(@PathVariable Long id) { - Student student = studentService.getStudentById(id); - if (student != null) { - return ResponseEntity.ok(student); - } else { - return ResponseEntity.notFound().build(); - } + @PostMapping + public ResponseEntity addStudent(@RequestBody Student student) { + studentService.save(student); + return ResponseEntity.ok("Student added successfully"); } - @PostMapping("/") - public ResponseEntity addStudent(@RequestBody Student student) { - studentService.addStudent(student); - return ResponseEntity.ok(student); + @PutMapping("/{studentNumber}") + public ResponseEntity updateStudent(@PathVariable String studentNumber, @RequestBody Student student) { + student.setStudentNumber(studentNumber); + studentService.update(student); + return ResponseEntity.ok("Student updated successfully"); } - @PutMapping("/{id}") - public ResponseEntity updateStudentPoints(@PathVariable Long id, @RequestParam int points) { - studentService.updateStudentPoints(id, points); - Student updatedStudent = studentService.getStudentById(id); - return ResponseEntity.ok(updatedStudent); + @DeleteMapping("/{id}") + public ResponseEntity deleteStudent(@PathVariable Long id) { + studentService.delete(id); + return ResponseEntity.ok("Student deleted successfully"); } + /** + * 查询学生积分排行榜,按积分从高到低排序,并分页返回 + * @param page 当前页数 + * @param size 每页显示的记录数 + * @return 排行榜分页数据 + */ + @GetMapping("/ranking") + public ResponseEntity> getStudentRanking(@RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size) { + List ranking = studentService.getStudentRanking(page, size); + return ResponseEntity.ok(ranking); + } + + /** + * 调整学生积分 + * @param studentNumber 学生编号 + * @param pointsRequest 包含积分调整值的请求 + * @return 成功或失败消息 + */ + @PutMapping("/{studentNumber}/adjustPoints") + public ResponseEntity adjustStudentPoints(@PathVariable String studentNumber, @RequestBody PointsRequest pointsRequest) { + try { + studentService.adjustPoints(studentNumber, pointsRequest.getPointsDelta()); + return ResponseEntity.ok("积分调整成功"); + } catch (Exception e) { + return ResponseEntity.status(500).body("积分调整失败:" + e.getMessage()); + } + } /** * 上传Excel文件并导入学生数据 * @param file Excel文件 @@ -55,7 +92,6 @@ public class StudentController { if (file.isEmpty()) { return "请上传有效的Excel文件"; } - try { studentService.importStudents(file); return "学生数据导入成功"; diff --git a/src/main/java/com/example/attendance/controller/TeacherController.java b/src/main/java/com/example/attendance/controller/TeacherController.java new file mode 100644 index 0000000..8e1fe04 --- /dev/null +++ b/src/main/java/com/example/attendance/controller/TeacherController.java @@ -0,0 +1,34 @@ +package com.example.attendance.controller; + +import com.example.attendance.service.TeacherService; +import com.example.attendance.service.impl.TeacherServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +@RestController +@RequestMapping("/api/teacher") +public class TeacherController { + + @Autowired + private TeacherService teacherService; + + @PostMapping("/register") + public ResponseEntity register(@RequestParam String username, @RequestParam String password) { + try { + teacherService.register(username, password); + return ResponseEntity.ok("注册成功"); // 成功时返回 200 状态和消息 + } catch (Exception e) { + return ResponseEntity.badRequest().body("注册失败: " + e.getMessage()); // 失败时返回 400 状态和错误信息 + } + } + + @PostMapping("/login") + public ResponseEntity login(@RequestParam String username, @RequestParam String password) { + try { + String token = teacherService.login(username, password); + return ResponseEntity.ok("登录成功, Token: " + token); // 成功时返回 200 状态和 JWT token + } catch (Exception e) { + return ResponseEntity.badRequest().body("登录失败: " + e.getMessage()); // 失败时返回 400 状态和错误信息 + } + } +} \ No newline at end of file diff --git a/src/main/java/com/example/attendance/entity/PointsRequest.java b/src/main/java/com/example/attendance/entity/PointsRequest.java new file mode 100644 index 0000000..078bb99 --- /dev/null +++ b/src/main/java/com/example/attendance/entity/PointsRequest.java @@ -0,0 +1,15 @@ +package com.example.attendance.entity; + +import java.math.BigDecimal; + +public class PointsRequest { + private BigDecimal pointsDelta; + + public BigDecimal getPointsDelta() { + return pointsDelta; + } + + public void setPointsDelta(BigDecimal pointsDelta) { + this.pointsDelta = pointsDelta; + } +} diff --git a/src/main/java/com/example/attendance/dto/StudentExcelDTO.java b/src/main/java/com/example/attendance/entity/RollCallResponse.java similarity index 51% rename from src/main/java/com/example/attendance/dto/StudentExcelDTO.java rename to src/main/java/com/example/attendance/entity/RollCallResponse.java index 5376b9a..285da0b 100644 --- a/src/main/java/com/example/attendance/dto/StudentExcelDTO.java +++ b/src/main/java/com/example/attendance/entity/RollCallResponse.java @@ -1,13 +1,18 @@ -package com.example.attendance.dto; +package com.example.attendance.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -@Data -@AllArgsConstructor +import java.math.BigDecimal; + @NoArgsConstructor -public class StudentExcelDTO { - private String name; +@AllArgsConstructor +@Data +public class RollCallResponse { private String studentId; + private String name; + private BigDecimal points; + private String message; // 显示结果信息 + } diff --git a/src/main/java/com/example/attendance/entity/RollCallSettings.java b/src/main/java/com/example/attendance/entity/RollCallSettings.java new file mode 100644 index 0000000..352ae8d --- /dev/null +++ b/src/main/java/com/example/attendance/entity/RollCallSettings.java @@ -0,0 +1,15 @@ +package com.example.attendance.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class RollCallSettings { + private boolean isRollCall; // 点名(true)或提问(false) + private boolean triggerRandomEvent; // 是否触发随机事件 + private boolean wheelOfFortune; // 是否开启命运轮盘 + +} diff --git a/src/main/java/com/example/attendance/entity/Student.java b/src/main/java/com/example/attendance/entity/Student.java index 71330f9..e70e23b 100644 --- a/src/main/java/com/example/attendance/entity/Student.java +++ b/src/main/java/com/example/attendance/entity/Student.java @@ -1,20 +1,43 @@ package com.example.attendance.entity; +import java.math.BigDecimal; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -// TODO:更新数据库表结构 -@NoArgsConstructor -@AllArgsConstructor -@Data public class Student { private Long id; - private String name; - private int initialPoints = 0; - private int currentPoints = 0; - private int correctAnswerStreak; // 连续答对次数 - private boolean skipNextCall; // 是否跳过下次点名 - private boolean borrowingPoints; // 是否借用积分 - private boolean increasedCallChance; // 点名概率是否增加 -} \ No newline at end of file + private String studentNumber; // 学号 + private String name; // 姓名 + private BigDecimal points; // 积分 + + // getters and setters + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getStudentNumber() { + return studentNumber; + } + + public void setStudentNumber(String studentNumber) { + this.studentNumber = studentNumber; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getPoints() { + return points; + } + + public void setPoints(BigDecimal points) { + this.points = points; + } +} diff --git a/src/main/java/com/example/attendance/dto/StudentDTO.java b/src/main/java/com/example/attendance/entity/Teacher.java similarity index 56% rename from src/main/java/com/example/attendance/dto/StudentDTO.java rename to src/main/java/com/example/attendance/entity/Teacher.java index f1b9bfa..7712c3c 100644 --- a/src/main/java/com/example/attendance/dto/StudentDTO.java +++ b/src/main/java/com/example/attendance/entity/Teacher.java @@ -1,14 +1,15 @@ -package com.example.attendance.dto; +package com.example.attendance.entity; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -@Data -@AllArgsConstructor @NoArgsConstructor -public class StudentDTO { +@AllArgsConstructor +@Data +public class Teacher { private Long id; - private String name; - private int currentPoints; + private String username; + private String password; + } diff --git a/src/main/java/com/example/attendance/listener/StudentExcelListener.java b/src/main/java/com/example/attendance/listener/StudentExcelListener.java deleted file mode 100644 index 8ef8a97..0000000 --- a/src/main/java/com/example/attendance/listener/StudentExcelListener.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.example.attendance.listener; - -import com.alibaba.excel.context.AnalysisContext; -import com.alibaba.excel.event.AnalysisEventListener; -import com.example.attendance.dto.StudentExcelDTO; -import com.example.attendance.entity.Student; -import com.example.attendance.mapper.StudentMapper; - -public class StudentExcelListener extends AnalysisEventListener { - private final StudentMapper studentMapper; - - public StudentExcelListener(StudentMapper studentMapper) { - this.studentMapper = studentMapper; - } - - @Override - public void invoke(StudentExcelDTO data, AnalysisContext context) { - Student student = new Student(); - student.setName(data.getName()); - student.setId(Long.valueOf(data.getStudentId())); - student.setCurrentPoints(0); // 初始积分为0 - studentMapper.insertStudent(student); - } - - @Override - public void doAfterAllAnalysed(AnalysisContext context) { - // 处理完成后的操作 - } -} diff --git a/src/main/java/com/example/attendance/mapper/StudentMapper.java b/src/main/java/com/example/attendance/mapper/StudentMapper.java index 417d18b..7249c6d 100644 --- a/src/main/java/com/example/attendance/mapper/StudentMapper.java +++ b/src/main/java/com/example/attendance/mapper/StudentMapper.java @@ -3,20 +3,47 @@ package com.example.attendance.mapper; import com.example.attendance.entity.Student; import org.apache.ibatis.annotations.*; +import java.math.BigDecimal; import java.util.List; @Mapper public interface StudentMapper { - @Select("SELECT * FROM students WHERE id = #{id}") - Student getStudentById(Long id); + @Select("SELECT * FROM student WHERE id = #{id}") + Student findById(Long id); - @Select("SELECT * FROM students") - List getAllStudents(); + @Select("SELECT * FROM student WHERE student_number = #{studentNumber}") + Student findByStudentNumber(String studentNumber); - @Insert("INSERT INTO students(name, initialPoints, currentPoints) VALUES(#{name}, #{initialPoints}, #{currentPoints})") - void insertStudent(Student student); + @Select("SELECT * FROM student") + List findAll(); - @Update("UPDATE students SET currentPoints = #{currentPoints} WHERE id = #{id}") - void updateStudentPoints(@Param("id") Long id, @Param("currentPoints") int currentPoints); + @Insert("INSERT INTO student (student_number, name, points) VALUES (#{studentNumber}, #{name}, #{points})") + @Options(useGeneratedKeys = true, keyProperty = "id") + void save(Student student); + + @Update("UPDATE student SET name = #{name}, points = #{points} WHERE student_number = #{studentNumber}") + void update(Student student); + + @Delete("DELETE FROM student WHERE id = #{id}") + void delete(Long id); + + @Insert({ + "" + }) + void saveStudents(List students); + + /** + * 按积分从高到低排序的分页查询 + * @param offset 分页查询的偏移量 + * @param size 每页显示的记录数 + * @return 排行榜学生列表 + */ + @Select("SELECT * FROM student ORDER BY points DESC LIMIT #{size} OFFSET #{offset}") + List findStudentsByRanking(@Param("offset") int offset, @Param("size") int size); } diff --git a/src/main/java/com/example/attendance/mapper/TeacherMapper.java b/src/main/java/com/example/attendance/mapper/TeacherMapper.java new file mode 100644 index 0000000..7283f7d --- /dev/null +++ b/src/main/java/com/example/attendance/mapper/TeacherMapper.java @@ -0,0 +1,15 @@ +package com.example.attendance.mapper; + +import com.example.attendance.entity.Teacher; +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +@Mapper +public interface TeacherMapper { + + @Insert("INSERT INTO teacher (username, password) VALUES (#{username}, #{password})") + void save(Teacher teacher); + + @Select("SELECT * FROM teacher WHERE username = #{username}") + Teacher findByUsername(String username); +} diff --git a/src/main/java/com/example/attendance/service/RollCallService.java b/src/main/java/com/example/attendance/service/RollCallService.java new file mode 100644 index 0000000..f4eaf59 --- /dev/null +++ b/src/main/java/com/example/attendance/service/RollCallService.java @@ -0,0 +1,10 @@ +package com.example.attendance.service; +import com.example.attendance.entity.RollCallResponse; +import com.example.attendance.entity.RollCallSettings; +import com.example.attendance.entity.Student; + +import java.util.List; + +public interface RollCallService { + RollCallResponse startRollCall(List students, RollCallSettings settings); // 处理点名逻辑 +} diff --git a/src/main/java/com/example/attendance/service/StudentService.java b/src/main/java/com/example/attendance/service/StudentService.java index 9d7cffb..35eefb0 100644 --- a/src/main/java/com/example/attendance/service/StudentService.java +++ b/src/main/java/com/example/attendance/service/StudentService.java @@ -1,15 +1,20 @@ package com.example.attendance.service; -import com.example.attendance.dto.StudentExcelDTO; import com.example.attendance.entity.Student; import org.springframework.web.multipart.MultipartFile; +import java.math.BigDecimal; import java.util.List; public interface StudentService { - void importStudents(MultipartFile file); - List getAllStudents(); - Student getStudentById(Long id); - void addStudent(Student student); - void updateStudentPoints(Long id, int points); + void importStudents(MultipartFile file) throws Exception ; + void adjustPoints(String studentNumber, BigDecimal pointsDelta); + List getStudentRanking(int page, int size); + Student findById(Long id); + Student findByStudentNumber(String studentNumber); + List findAll(); + void save(Student student); + void update(Student student); + void delete(Long id); + } diff --git a/src/main/java/com/example/attendance/service/TeacherService.java b/src/main/java/com/example/attendance/service/TeacherService.java new file mode 100644 index 0000000..c88c46a --- /dev/null +++ b/src/main/java/com/example/attendance/service/TeacherService.java @@ -0,0 +1,23 @@ +package com.example.attendance.service; + +public interface TeacherService { + + /** + * 注册新教师 + * + * @param username 教师的用户名 + * @param password 教师的密码 + * @throws Exception 注册过程中可能抛出的异常 + */ + void register(String username, String password) throws Exception; + + /** + * 教师登录 + * + * @param username 教师的用户名 + * @param password 教师的密码 + * @return 返回 JWT token + * @throws Exception 登录失败时抛出的异常 + */ + String login(String username, String password) throws Exception; +} diff --git a/src/main/java/com/example/attendance/service/impl/RollCallServiceImpl.java b/src/main/java/com/example/attendance/service/impl/RollCallServiceImpl.java new file mode 100644 index 0000000..2a97d9c --- /dev/null +++ b/src/main/java/com/example/attendance/service/impl/RollCallServiceImpl.java @@ -0,0 +1,110 @@ +package com.example.attendance.service.impl; + +import com.example.attendance.entity.RollCallResponse; +import com.example.attendance.entity.RollCallSettings; +import com.example.attendance.entity.Student; +import com.example.attendance.service.RollCallService; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Random; + +@Service +public class RollCallServiceImpl implements RollCallService { + + @Override + public RollCallResponse startRollCall(List students, RollCallSettings settings) { + // 1. 根据设定选择点名或提问模式 + String mode = settings.isRollCall() ? "点名" : "提问"; + System.out.println("当前模式:" + mode); + + // 2. 处理命运轮盘 (所有人概率相等) + if (settings.isWheelOfFortune()) { + return handleWheelOfFortune(students); + } + + // 3. 正常点名逻辑,使用权重随机选择学生 + Student selectedStudent = selectWeightedRandomStudent(students); + RollCallResponse response = new RollCallResponse(); + response.setStudentId(selectedStudent.getStudentNumber()); + response.setName(selectedStudent.getName()); + response.setPoints(selectedStudent.getPoints()); + + // 4. 判断是否触发随机事件 + if (settings.isTriggerRandomEvent()) { + response.setMessage("触发了随机事件: " + triggerRandomEvent()); + } else { + response.setMessage("没有触发随机事件," + selectedStudent.getName() + " 被点了!"); + } + + return response; + } + + // 权重随机选择学生,积分越高概率越低 + private Student selectWeightedRandomStudent(List students) { + Random random = new Random(); + int totalWeight = 0; + + // 计算所有学生的总权重,假设权重为 100 - currentPoints + for (Student student : students) { + int weight = 100 - student.getPoints().intValue(); + if (weight < 1) { + weight = 1; // 确保权重最低为1 + } + totalWeight += weight; // 累计总权重 + } + + // 如果 totalWeight 为 0 或负数,则无法进行随机选择 + if (totalWeight <= 0) { + throw new IllegalArgumentException("总权重必须为正数"); + } + + // 随机生成一个 0 到 totalWeight 之间的随机数 + int randomIndex = random.nextInt(totalWeight); + int currentWeightSum = 0; + + // 遍历学生列表,根据累计权重确定选中的学生 + for (Student student : students) { + int weight = 100 - student.getPoints().intValue(); + if (weight < 1) { + weight = 1; + } + + currentWeightSum += weight; + // 当累计权重超过随机数时,选择当前学生 + if (currentWeightSum > randomIndex) { + return student; + } + } + + // 如果没有选中任何学生,兜底返回第一个学生(理论上不会发生) + return students.get(0); + } + + // 命运轮盘处理(所有学生概率相等) + private RollCallResponse handleWheelOfFortune(List students) { + Random random = new Random(); + Student selectedStudent = students.get(random.nextInt(students.size())); + + RollCallResponse response = new RollCallResponse(); + response.setStudentId(selectedStudent.getStudentNumber()); + response.setName(selectedStudent.getName()); + response.setPoints(selectedStudent.getPoints()); + response.setMessage("命运轮盘: " + selectedStudent.getName() + " 被选中了!"); + + return response; + } + + // 触发随机事件:赌徒事件或倒霉事件 + private String triggerRandomEvent() { + Random random = new Random(); + int eventType = random.nextInt(2); // 0表示赌徒事件,1表示倒霉事件 + + if (eventType == 0) { + return "赌徒事件"; // 触发赌徒事件 + } else { + return "倒霉事件"; // 触发倒霉事件 + } + } +} diff --git a/src/main/java/com/example/attendance/service/impl/StudentServiceImpl.java b/src/main/java/com/example/attendance/service/impl/StudentServiceImpl.java index 608157d..af44988 100644 --- a/src/main/java/com/example/attendance/service/impl/StudentServiceImpl.java +++ b/src/main/java/com/example/attendance/service/impl/StudentServiceImpl.java @@ -1,16 +1,19 @@ package com.example.attendance.service.impl; -import com.alibaba.excel.EasyExcel; -import com.example.attendance.dto.StudentExcelDTO; import com.example.attendance.entity.Student; -import com.example.attendance.listener.StudentExcelListener; import com.example.attendance.mapper.StudentMapper; import com.example.attendance.service.StudentService; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.InputStream; +import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; @Service @@ -20,33 +23,92 @@ public class StudentServiceImpl implements StudentService { private StudentMapper studentMapper; @Override - public void importStudents(MultipartFile file) { - try (InputStream inputStream = file.getInputStream()) { - EasyExcel.read(inputStream, StudentExcelDTO.class, new StudentExcelListener(studentMapper)) - .sheet() - .doRead(); - } catch (Exception e) { - throw new RuntimeException("从Excel导入学生数据失败", e); + public void importStudents(MultipartFile file) throws Exception { + List students = new ArrayList<>(); + + // 获取上传的 Excel 文件流 + try (InputStream inputStream = file.getInputStream(); + Workbook workbook = new XSSFWorkbook(inputStream)) { + + // 读取第一个工作表 + Sheet sheet = workbook.getSheetAt(0); + + // 遍历每一行,从第二行开始(假设第一行是标题) + for (int i = 1; i <= sheet.getLastRowNum(); i++) { + Row row = sheet.getRow(i); + if (row == null) continue; + + // 读取学生数据 + String studentNumber = row.getCell(0).getStringCellValue(); + String name = row.getCell(1).getStringCellValue(); + BigDecimal points = new BigDecimal(row.getCell(2).getNumericCellValue()); + + Student student = new Student(); + student.setStudentNumber(studentNumber); + student.setName(name); + student.setPoints(points); + + students.add(student); + } + } + + // 将学生列表保存到数据库 + studentMapper.saveStudents(students); + } + + /** + * 调整指定学生的积分 + * @param studentNumber 学生编号 + * @param pointsDelta 要加或减的积分值 + */ + public void adjustPoints(String studentNumber, BigDecimal pointsDelta) { + Student student = studentMapper.findByStudentNumber(studentNumber); + if (student == null) { + throw new RuntimeException("未找到该学生"); } + BigDecimal updatedPoints = student.getPoints().add(pointsDelta); + student.setPoints(updatedPoints); + studentMapper.update(student); + } + + /** + * 获取学生积分排行榜,按积分从高到低排序,并进行分页 + * @param page 当前页数 + * @param size 每页显示的记录数 + * @return 学生排行榜 + */ + public List getStudentRanking(int page, int size) { + int offset = page * size; + return studentMapper.findStudentsByRanking(offset, size); + } + + @Override + public Student findById(Long id) { + return studentMapper.findById(id); + } + + @Override + public Student findByStudentNumber(String studentNumber) { + return studentMapper.findByStudentNumber(studentNumber); } @Override - public List getAllStudents() { - return studentMapper.getAllStudents(); + public List findAll() { + return studentMapper.findAll(); } @Override - public Student getStudentById(Long id) { - return studentMapper.getStudentById(id); + public void save(Student student) { + studentMapper.save(student); } @Override - public void addStudent(Student student) { - studentMapper.insertStudent(student); + public void update(Student student) { + studentMapper.update(student); } @Override - public void updateStudentPoints(Long id, int points) { - studentMapper.updateStudentPoints(id, points); + public void delete(Long id) { + studentMapper.delete(id); } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/attendance/service/impl/TeacherServiceImpl.java b/src/main/java/com/example/attendance/service/impl/TeacherServiceImpl.java new file mode 100644 index 0000000..7e9bd80 --- /dev/null +++ b/src/main/java/com/example/attendance/service/impl/TeacherServiceImpl.java @@ -0,0 +1,37 @@ +package com.example.attendance.service.impl; + +import com.example.attendance.mapper.TeacherMapper; +import com.example.attendance.entity.Teacher; +import com.example.attendance.service.TeacherService; +import com.example.attendance.util.JWTUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +@Service +public class TeacherServiceImpl implements TeacherService { + + @Autowired + private TeacherMapper teacherMapper; + + @Autowired + private PasswordEncoder passwordEncoder; // 使用 PasswordEncoder 接口 + + @Override + public void register(String username, String password) { + Teacher teacher = new Teacher(); + teacher.setUsername(username); + teacher.setPassword(passwordEncoder.encode(password)); // 密码加密 + teacherMapper.save(teacher); + } + + @Override + public String login(String username, String password) { + Teacher teacher = teacherMapper.findByUsername(username); + if (teacher == null || !passwordEncoder.matches(password, teacher.getPassword())) { + throw new RuntimeException("用户名或密码不正确"); + } + // 返回 JWT token + return JWTUtil.generateToken(teacher); + } +} diff --git a/src/main/java/com/example/attendance/util/JWTUtil.java b/src/main/java/com/example/attendance/util/JWTUtil.java new file mode 100644 index 0000000..b5ab66c --- /dev/null +++ b/src/main/java/com/example/attendance/util/JWTUtil.java @@ -0,0 +1,43 @@ +package com.example.attendance.util; + +import com.example.attendance.entity.Teacher; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.security.Keys; + +import javax.crypto.SecretKey; +import java.util.Date; + +public class JWTUtil { + + private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); + + public static String generateToken(Teacher teacher) { + return Jwts.builder() + .setSubject(teacher.getUsername()) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + 60 * 60 * 1000)) // Token 有效期 1 小时 + .signWith(SignatureAlgorithm.HS256, SECRET_KEY) + .compact(); + } + + public static Claims extractClaims(String token) { + try { + return Jwts.parser() + .setSigningKey(SECRET_KEY) + .parseClaimsJws(token) + .getBody(); + } catch (Exception e) { + throw new RuntimeException("Invalid JWT token", e); // 捕获并抛出异常 + } + } + + public static String getUsernameFromToken(String token) { + return extractClaims(token).getSubject(); + } + + public static boolean isTokenExpired(String token) { + return extractClaims(token).getExpiration().before(new Date()); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 0ac666c..32b3c0b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,4 +4,4 @@ spring.datasource.username=root spring.datasource.password=123456789jk spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver -mybatis.mapper-locations=classpath:mappers/*.xml + diff --git a/src/test/java/com/example/attendance/PasswordEncryptor.java b/src/test/java/com/example/attendance/PasswordEncryptor.java new file mode 100644 index 0000000..e9f2f49 --- /dev/null +++ b/src/test/java/com/example/attendance/PasswordEncryptor.java @@ -0,0 +1,24 @@ +package com.example.attendance; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; + +public class PasswordEncryptor { + public static void main(String[] args) { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + // 这里使用你希望的明文密码 + String rawPassword1 = "123456"; + String rawPassword2 = "654321"; + String rawPassword3 = "admin123"; + + // 加密密码 + String encodedPassword1 = passwordEncoder.encode(rawPassword1); + String encodedPassword2 = passwordEncoder.encode(rawPassword2); + String encodedPassword3 = passwordEncoder.encode(rawPassword3); + + // 打印加密后的密码 + System.out.println("teacher1 密码: " + encodedPassword1); + System.out.println("teacher2 密码: " + encodedPassword2); + System.out.println("teacher3 密码: " + encodedPassword3); + } +}