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);
+ }
+}