Compare commits

..

No commits in common. "dcb5e9d61ac303856edca7f069e93f2d9fc67dc3" and "2ba0e5c6a77daf4961bac5c3e5107b012e78d3f0" have entirely different histories.

32 changed files with 105 additions and 2449 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="KubernetesApiProvider"><![CDATA[{}]]></component> <component name="KubernetesApiProvider">{}</component>
<component name="MavenProjectsManager"> <component name="MavenProjectsManager">
<option name="originalFiles"> <option name="originalFiles">
<list> <list>

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
img_1.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

102
pom.xml
View File

@ -6,28 +6,22 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.campus</groupId> <groupId>com.campus</groupId>
<artifactId>student-attendance</artifactId> <artifactId>campus-attendance</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<spring.version>5.3.23</spring.version> <spring.version>5.3.23</spring.version>
<mybatis.version>3.5.11</mybatis.version> <mybatis.version>3.5.11</mybatis.version>
<mybatis.spring.version>2.1.1</mybatis.spring.version> <mybatis.spring.version>2.0.7</mybatis.spring.version> <!-- 修正后的行 -->
<mysql.version>8.0.33</mysql.version>
<druid.version>1.2.15</druid.version>
<servlet.version>4.0.1</servlet.version>
<jstl.version>1.2</jstl.version>
<jackson.version>2.14.2</jackson.version>
<slf4j.version>2.0.7</slf4j.version>
<logback.version>1.4.7</logback.version>
</properties> </properties>
<dependencies> <dependencies>
<!-- Spring Framework --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId> <artifactId>spring-context</artifactId>
@ -43,11 +37,6 @@
<artifactId>spring-jdbc</artifactId> <artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version> <version>${spring.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- MyBatis --> <!-- MyBatis -->
<dependency> <dependency>
@ -61,86 +50,73 @@
<version>${mybatis.spring.version}</version> <version>${mybatis.spring.version}</version>
</dependency> </dependency>
<!-- Database --> <!-- 数据库 -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version> <version>8.0.33</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid</artifactId> <artifactId>druid</artifactId>
<version>${druid.version}</version> <version>1.2.15</version>
</dependency> </dependency>
<!-- Servlet & JSP --> <!-- Servlet API -->
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId> <artifactId>javax.servlet-api</artifactId>
<version>${servlet.version}</version> <version>4.0.1</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.servlet.jsp</groupId> <groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId> <artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version> <version>2.3.3</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- JSON Processing --> <!-- JSON -->
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version> <version>2.14.1</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<!-- Testing (可选) -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<finalName>student-attendance</finalName> <finalName>campus-attendance</finalName>
<plugins> <plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>${project.build.sourceEncoding}</encoding>
</configuration>
</plugin>
<!-- 用于将依赖包复制到lib目录 -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId> <artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version> <version>3.3.2</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration> <configuration>
<failOnMissingWebXml>false</failOnMissingWebXml> <source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
<!-- 新增 JDK 17 编译参数 -->
<compilerArgs>
<arg>--enable-preview</arg>
</compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>

191
readme.md
View File

@ -1,184 +1,35 @@
--用户表 (user) MySQL中创建数据库和表
CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
role ENUM('ADMIN', 'TEACHER', 'STUDENT') NOT NULL,
created_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
--老师表 (teacher)
CREATE TABLE teacher (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT UNIQUE NOT NULL,
name VARCHAR(50) NOT NULL,
teacher_id VARCHAR(20) UNIQUE NOT NULL,
department VARCHAR(100),
title VARCHAR(50),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
);
--学生表 (student) sql
-- 创建数据库
CREATE DATABASE campus_attendance DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE campus_attendance;
-- 创建学生表
CREATE TABLE student ( CREATE TABLE student (
id INT PRIMARY KEY AUTO_INCREMENT, id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT UNIQUE NOT NULL,
name VARCHAR(50) NOT NULL, name VARCHAR(50) NOT NULL,
student_id VARCHAR(20) UNIQUE NOT NULL, student_id VARCHAR(20) NOT NULL UNIQUE,
class_name VARCHAR(50), class VARCHAR(50),
major VARCHAR(100), major VARCHAR(100)
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE
); );
--课程表 (course) -- 创建用户表
CREATE TABLE course ( CREATE TABLE user (
id INT PRIMARY KEY AUTO_INCREMENT, id INT PRIMARY KEY AUTO_INCREMENT,
course_name VARCHAR(100) NOT NULL, username VARCHAR(50) NOT NULL UNIQUE,
course_code VARCHAR(20) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL,
teacher_id INT NOT NULL, role VARCHAR(20) NOT NULL
description TEXT,
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (teacher_id) REFERENCES teacher(id) ON DELETE CASCADE
); );
--学生-课程关联表 (student_course) -- 插入测试数据
CREATE TABLE student_course ( INSERT INTO student (name, student_id, class, major) VALUES
id INT PRIMARY KEY AUTO_INCREMENT, ('张三', '2023001', '计算机1班', '计算机科学与技术'),
student_id INT NOT NULL, ('李四', '2023002', '软件1班', '软件工程');
course_id INT NOT NULL,
enroll_date DATE NOT NULL,
status ENUM('ACTIVE', 'COMPLETED', 'DROPPED') DEFAULT 'ACTIVE',
UNIQUE KEY uk_student_course (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE,
FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE
);
--考勤记录表 (attendance_record)
CREATE TABLE attendance_record (
id INT PRIMARY KEY AUTO_INCREMENT,
student_id INT NOT NULL,
course_id INT NOT NULL,
attendance_date DATE NOT NULL,
attendance_time TIME NOT NULL,
status ENUM('PRESENT', 'ABSENT', 'LATE') NOT NULL,
remarks TEXT,
created_by INT NOT NULL, -- 记录创建者老师ID
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_student_course_date (student_id, course_id, attendance_date),
FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE,
FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE,
FOREIGN KEY (created_by) REFERENCES teacher(id) ON DELETE CASCADE
);
老师-学生管理关系表 (teacher_student)
CREATE TABLE teacher_student (
id INT PRIMARY KEY AUTO_INCREMENT,
teacher_id INT NOT NULL,
student_id INT NOT NULL,
relationship_type ENUM('ADVISOR', 'INSTRUCTOR') NOT NULL,
start_date DATE NOT NULL,
end_date DATE,
UNIQUE KEY uk_teacher_student (teacher_id, student_id, relationship_type),
FOREIGN KEY (teacher_id) REFERENCES teacher(id) ON DELETE CASCADE,
FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE
);
--系统日志表 (system_log)
CREATE TABLE system_log (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
action VARCHAR(100) NOT NULL,
target_type VARCHAR(50) NOT NULL,
target_id INT,
description TEXT,
ip_address VARCHAR(45),
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user(id)
);
-- 以下是插入的数据
INSERT INTO user (username, password, role) VALUES INSERT INTO user (username, password, role) VALUES
('admin', 'admin123', 'ADMIN'), ('admin', 'admin123', 'ADMIN'),
('teacher1', 'teacher123', 'TEACHER'), ('teacher1', 'teacher123', 'TEACHER');
('teacher2', 'teacher456', 'TEACHER'),
('student1', 'student123', 'STUDENT'),
('student2', 'student456', 'STUDENT'),
('student3', 'student789', 'STUDENT'),
('student4', 'student000', 'STUDENT');
INSERT INTO teacher (user_id, name, teacher_id, department, title) VALUES ![img.png](img.png)
(2, '张老师', 'T001', '计算机科学系', '副教授'),
(3, '李老师', 'T002', '软件工程系', '讲师');
INSERT INTO student (user_id, name, student_id, class_name, major) VALUES
(4, '张三', 'S2024001', '计算机1班', '计算机科学与技术'),
(5, '李四', 'S2024002', '计算机1班', '计算机科学与技术'),
(6, '王五', 'S2024003', '软件工程1班', '软件工程'),
(7, '赵六', 'S2024004', '软件工程1班', '软件工程');
INSERT INTO course (course_name, course_code, teacher_id, description) VALUES
('Java程序设计', 'CS001', 1, 'Java语言基础与面向对象编程'),
('数据库原理', 'CS002', 1, '数据库系统原理与应用'),
('Web开发技术', 'SE001', 2, '前端与后端Web开发技术'),
('软件工程', 'SE002', 2, '软件开发流程与方法论');
INSERT INTO student_course (student_id, course_id, enroll_date, status) VALUES
-- 张三选课
(1, 1, '2024-09-01', 'ACTIVE'),
(1, 2, '2024-09-01', 'ACTIVE'),
-- 李四选课
(2, 1, '2024-09-01', 'ACTIVE'),
(2, 3, '2024-09-01', 'ACTIVE'),
-- 王五选课
(3, 3, '2024-09-01', 'ACTIVE'),
(3, 4, '2024-09-01', 'ACTIVE'),
-- 赵六选课
(4, 2, '2024-09-01', 'ACTIVE'),
(4, 4, '2024-09-01', 'ACTIVE');
INSERT INTO attendance_record (student_id, course_id, attendance_date, attendance_time, status, remarks, created_by) VALUES
-- Java程序设计考勤记录
(1, 1, '2024-10-01', '08:30:00', 'PRESENT', '按时到课', 1),
(2, 1, '2024-10-01', '08:45:00', 'LATE', '迟到15分钟', 1),
(1, 1, '2024-10-08', '08:30:00', 'PRESENT', '', 1),
(2, 1, '2024-10-08', '08:30:00', 'ABSENT', '请假', 1),
-- 数据库原理考勤记录
(1, 2, '2024-10-02', '10:00:00', 'PRESENT', '', 1),
(4, 2, '2024-10-02', '10:00:00', 'PRESENT', '', 1),
-- Web开发技术考勤记录
(2, 3, '2024-10-03', '14:00:00', 'PRESENT', '', 2),
(3, 3, '2024-10-03', '14:10:00', 'LATE', '交通堵塞', 2),
-- 软件工程考勤记录
(3, 4, '2024-10-04', '16:00:00', 'PRESENT', '', 2),
(4, 4, '2024-10-04', '16:00:00', 'PRESENT', '', 2);
INSERT INTO teacher_student (teacher_id, student_id, relationship_type, start_date) VALUES
-- 张老师管理计算机1班学生
(1, 1, 'ADVISOR', '2024-09-01'),
(1, 2, 'ADVISOR', '2024-09-01'),
-- 李老师管理软件工程1班学生
(2, 3, 'ADVISOR', '2024-09-01'),
(2, 4, 'ADVISOR', '2024-09-01'),
-- 授课关系
(1, 1, 'INSTRUCTOR', '2024-09-01'),
(1, 2, 'INSTRUCTOR', '2024-09-01'),
(1, 4, 'INSTRUCTOR', '2024-09-01'),
(2, 2, 'INSTRUCTOR', '2024-09-01'),
(2, 3, 'INSTRUCTOR', '2024-09-01'),
(2, 4, 'INSTRUCTOR', '2024-09-01');
INSERT INTO system_log (user_id, action, target_type, target_id, description, ip_address) VALUES
(1, 'LOGIN', 'SYSTEM', NULL, '管理员登录系统', '192.168.1.100'),
(2, 'CREATE', 'ATTENDANCE', 1, '创建考勤记录', '192.168.1.101'),
(2, 'UPDATE', 'ATTENDANCE', 2, '更新考勤状态', '192.168.1.101'),
(3, 'CREATE', 'COURSE', 3, '创建新课程', '192.168.1.102'),
(1, 'DELETE', 'USER', 5, '删除用户', '192.168.1.100');

View File

@ -1,206 +0,0 @@
// com/campus/controller/AttendanceController.java
package com.campus.controller;
import com.campus.entity.AttendanceRecord;
import com.campus.entity.User;
import com.campus.service.AttendanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import java.util.*;
@RestController
@RequestMapping("/api/attendance")
public class AttendanceController {
@Autowired
private AttendanceService attendanceService;
/**
* 记录考勤教师使用
*/
@PostMapping("/record")
public Map<String, Object> recordAttendance(@RequestBody Map<String, Object> params, // 改为Object类型
HttpSession session) {
Map<String, Object> result = new HashMap<>();
// 检查用户是否登录且为教师
User user = (User) session.getAttribute("currentUser");
if (user == null) {
result.put("success", false);
result.put("message", "请先登录");
return result;
}
if (!"TEACHER".equals(user.getRole()) && !"ADMIN".equals(user.getRole())) {
result.put("success", false);
result.put("message", "只有教师和管理员可以记录考勤");
return result;
}
try {
// 类型转换处理
Integer studentId = null;
Integer courseId = null;
try {
studentId = Integer.parseInt(params.get("studentId").toString());
courseId = Integer.parseInt(params.get("courseId").toString());
} catch (NumberFormatException e) {
result.put("success", false);
result.put("message", "学生ID或课程ID格式错误");
return result;
}
String status = (String) params.get("status");
if (studentId == null || courseId == null || status == null) {
result.put("success", false);
result.put("message", "参数不完整");
return result;
}
AttendanceRecord record = new AttendanceRecord();
record.setStudentId(studentId); // 使用Integer类型
record.setCourseId(courseId); // 使用courseId
record.setAttendanceDate(new Date());
record.setStatus(status);
record.setRemarks((String) params.get("remarks"));
record.setCreatedBy(user.getId()); // 使用用户ID而不是用户名
boolean success = attendanceService.recordAttendance(record);
if (success) {
result.put("success", true);
result.put("message", "考勤记录成功");
} else {
result.put("success", false);
result.put("message", "考勤记录失败");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", e.getMessage());
}
return result;
}
/**
* 补签考勤教师使用
*/
@PostMapping("/makeup")
public Map<String, Object> makeupAttendance(@RequestBody Map<String, Object> params, // 改为Object类型
HttpSession session) {
Map<String, Object> result = new HashMap<>();
User user = (User) session.getAttribute("currentUser");
if (user == null || !"TEACHER".equals(user.getRole())) {
result.put("success", false);
result.put("message", "只有教师可以进行补签");
return result;
}
try {
AttendanceRecord record = new AttendanceRecord();
// 类型转换处理
Integer studentId = Integer.parseInt(params.get("studentId").toString());
Integer courseId = Integer.parseInt(params.get("courseId").toString());
record.setStudentId(studentId);
record.setCourseId(courseId);
record.setStatus((String) params.get("status"));
record.setRemarks((String) params.get("remarks"));
record.setCreatedBy(user.getId()); // 使用用户ID
// 解析日期
String dateStr = (String) params.get("attendanceDate");
if (dateStr != null) {
record.setAttendanceDate(new Date(Long.parseLong(dateStr)));
} else {
record.setAttendanceDate(new Date());
}
boolean success = attendanceService.makeUpAttendance(record);
if (success) {
result.put("success", true);
result.put("message", "补签成功");
} else {
result.put("success", false);
result.put("message", "补签失败");
}
} catch (Exception e) {
result.put("success", false);
result.put("message", e.getMessage());
}
return result;
}
/**
* 获取我的考勤记录学生使用
*/
@GetMapping("/my")
public Map<String, Object> getMyAttendance(HttpSession session) {
Map<String, Object> result = new HashMap<>();
User user = (User) session.getAttribute("currentUser");
if (user == null) {
result.put("success", false);
result.put("message", "请先登录");
return result;
}
if ("STUDENT".equals(user.getRole())) {
// 需要建立User和Student的关联这里假设可以通过用户ID查询学生ID
// 实际实现需要在service层处理
List<AttendanceRecord> records = attendanceService.getAttendanceByStudentId(user.getId());
result.put("success", true);
result.put("records", records);
} else {
result.put("success", false);
result.put("message", "只有学生可以查看个人考勤");
}
return result;
}
/**
* 查询考勤记录教师/管理员使用
*/
@GetMapping("/query")
public Map<String, Object> queryAttendance(
@RequestParam(required = false) Integer studentId, // 改为Integer类型
@RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd") Date date,
HttpSession session) {
Map<String, Object> result = new HashMap<>();
User user = (User) session.getAttribute("currentUser");
if (user == null || (!"TEACHER".equals(user.getRole()) && !"ADMIN".equals(user.getRole()))) {
result.put("success", false);
result.put("message", "权限不足");
return result;
}
List<AttendanceRecord> records;
if (studentId != null) {
records = attendanceService.getAttendanceByStudentId(studentId);
} else if (date != null) {
records = attendanceService.getAttendanceByDate(date);
} else {
// 默认查询今天的考勤
records = attendanceService.getAttendanceByDate(new Date());
}
result.put("success", true);
result.put("records", records);
return result;
}
// 其他方法保持不变...
}

View File

@ -1,13 +1,8 @@
// com/campus/controller/PageController.java
package com.campus.controller; package com.campus.controller;
import com.campus.entity.User;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import javax.servlet.http.HttpSession;
@Controller @Controller
public class PageController { public class PageController {
@ -20,44 +15,4 @@ public class PageController {
public String studentList() { public String studentList() {
return "student_list"; return "student_list";
} }
@GetMapping("/login")
public String login() {
return "login";
}
@GetMapping("/register")
public String register() {
return "register";
}
@GetMapping("/admin")
public String adminPage(HttpSession session, Model model) {
User user = (User) session.getAttribute("currentUser");
if (user == null || !"ADMIN".equals(user.getRole())) {
return "redirect:/login";
}
model.addAttribute("user", user);
return "admin";
}
@GetMapping("/teacher")
public String teacherPage(HttpSession session, Model model) {
User user = (User) session.getAttribute("currentUser");
if (user == null || !"TEACHER".equals(user.getRole())) {
return "redirect:/login";
}
model.addAttribute("user", user);
return "teacher";
}
@GetMapping("/student")
public String studentPage(HttpSession session, Model model) {
User user = (User) session.getAttribute("currentUser");
if (user == null || !"STUDENT".equals(user.getRole())) {
return "redirect:/login";
}
model.addAttribute("user", user);
return "student";
}
} }

View File

@ -1,4 +1,3 @@
// com/campus/controller/StudentController.java
package com.campus.controller; package com.campus.controller;
import com.campus.entity.Student; import com.campus.entity.Student;
@ -6,10 +5,9 @@ import com.campus.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
// StudentController.java
@RestController @RestController
@RequestMapping("/student") @RequestMapping("/student")
public class StudentController { public class StudentController {
@ -21,67 +19,9 @@ public class StudentController {
return studentService.listAll(); return studentService.listAll();
} }
@GetMapping("/{studentId}")
public Map<String, Object> getByStudentId(@PathVariable Integer studentId) {
Map<String, Object> result = new HashMap<>();
Student student = studentService.getByStudentId(studentId);
if (student != null) {
result.put("success", true);
result.put("student", student);
} else {
result.put("success", false);
result.put("message", "学生不存在");
}
return result;
}
@PostMapping("/add") @PostMapping("/add")
public Map<String, Object> add(@RequestBody Student student) { public String add(@RequestBody Student student) {
Map<String, Object> result = new HashMap<>(); studentService.add(student);
return "success";
try {
studentService.add(student);
result.put("success", true);
result.put("message", "添加成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "添加失败:" + e.getMessage());
}
return result;
}
@PutMapping("/update")
public Map<String, Object> update(@RequestBody Student student) {
Map<String, Object> result = new HashMap<>();
try {
studentService.update(student);
result.put("success", true);
result.put("message", "更新成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "更新失败:" + e.getMessage());
}
return result;
}
@DeleteMapping("/delete/{id}")
public Map<String, Object> delete(@PathVariable Integer id) {
Map<String, Object> result = new HashMap<>();
try {
studentService.delete(id);
result.put("success", true);
result.put("message", "删除成功");
} catch (Exception e) {
result.put("success", false);
result.put("message", "删除失败:" + e.getMessage());
}
return result;
} }
} }

View File

@ -1,141 +0,0 @@
// com/campus/controller/UserController.java
package com.campus.controller;
import com.campus.entity.User;
import com.campus.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/user")
public class UserController {
@Autowired
private UserService userService;
/**
* 登录 - 使用POST方法
*/
@PostMapping("/login")
public Map<String, Object> login(@RequestBody Map<String, String> params, HttpSession session) {
Map<String, Object> result = new HashMap<>();
String username = params.get("username");
String password = params.get("password");
if (username == null || username.trim().isEmpty() ||
password == null || password.trim().isEmpty()) {
result.put("success", false);
result.put("message", "用户名和密码不能为空");
return result;
}
User loginUser = userService.login(username, password);
if (loginUser != null) {
// 存储用户信息到session移除密码
User sessionUser = new User();
sessionUser.setId(loginUser.getId());
sessionUser.setUsername(loginUser.getUsername());
sessionUser.setRole(loginUser.getRole());
session.setAttribute("currentUser", sessionUser);
result.put("success", true);
result.put("message", "登录成功");
result.put("role", loginUser.getRole());
result.put("userId", loginUser.getId());
} else {
result.put("success", false);
result.put("message", "用户名或密码错误");
}
return result;
}
/**
* 注册 - 使用POST方法
*/
@PostMapping("/register")
public Map<String, Object> register(@RequestBody Map<String, String> params) {
Map<String, Object> result = new HashMap<>();
String username = params.get("username");
String password = params.get("password");
String role = params.get("role");
// 验证参数
if (username == null || username.trim().isEmpty()) {
result.put("success", false);
result.put("message", "用户名不能为空");
return result;
}
if (password == null || password.trim().isEmpty()) {
result.put("success", false);
result.put("message", "密码不能为空");
return result;
}
if (role == null) {
role = "STUDENT"; // 默认角色为学生
}
// 验证角色是否合法
if (!"ADMIN".equals(role) && !"TEACHER".equals(role) && !"STUDENT".equals(role)) {
result.put("success", false);
result.put("message", "角色必须是ADMIN、TEACHER或STUDENT");
return result;
}
// 创建用户对象
User user = new User();
user.setUsername(username);
user.setPassword(password); // Service层会加密
user.setRole(role);
boolean success = userService.register(user);
if (success) {
result.put("success", true);
result.put("message", "注册成功");
} else {
result.put("success", false);
result.put("message", "用户名已存在");
}
return result;
}
/**
* 退出登录
*/
@PostMapping("/logout")
public Map<String, Object> logout(HttpSession session) {
Map<String, Object> result = new HashMap<>();
session.invalidate();
result.put("success", true);
result.put("message", "退出成功");
return result;
}
/**
* 获取当前登录用户信息
*/
@GetMapping("/current")
public Map<String, Object> getCurrentUser(HttpSession session) {
Map<String, Object> result = new HashMap<>();
User user = (User) session.getAttribute("currentUser");
if (user != null) {
result.put("success", true);
result.put("user", user);
} else {
result.put("success", false);
result.put("message", "未登录");
}
return result;
}
}

View File

@ -1,50 +0,0 @@
// com/campus/entity/AttendanceRecord.java
package com.campus.entity;
import java.util.Date;
public class AttendanceRecord {
private Integer id;
private Integer studentId;
private Integer courseID;
private Date attendanceDate;
private Date attendanceTime;
private String status; // PRESENT/ABSENT/LATE
private String remarks;
private Integer createdBy;
private Date createTime;
// 添加关联对象用于查询显示
// private Student student;
// private Course course;
// private Teacher teacher;
// getters and setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public Integer getStudentId() { return studentId; }
public void setStudentId(Integer studentId) { this.studentId = studentId; }
public Integer getCourseId() { return courseID; }
public void setCourseId(Integer courseID) { this.courseID = courseID; }
public Date getAttendanceDate() { return attendanceDate; }
public void setAttendanceDate(Date attendanceDate) { this.attendanceDate = attendanceDate; }
public Date getAttendanceTime() { return attendanceTime; }
public void setAttendanceTime(Date attendanceTime) { this.attendanceTime = attendanceTime; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public String getRemarks() { return remarks; }
public void setRemarks(String remarks) { this.remarks = remarks; }
public Integer getCreatedBy() { return createdBy; }
public void setCreatedBy(Integer createdBy) { this.createdBy = createdBy; }
public Date getCreateTime() { return createTime; }
public void setCreateTime(Date createTime) { this.createTime = createTime; }
}

View File

@ -1,26 +0,0 @@
// com/campus/entity/Course.java
package com.campus.entity;
public class Course {
private Integer id;
private String courseName;
private String courseCode;
private Integer teacherId;
private String description;
// getters and setters
public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
public String getCourseName() { return courseName; }
public void setCourseName(String courseName) { this.courseName = courseName; }
public String getCourseCode() { return courseCode; }
public void setCourseCode(String courseCode) { this.courseCode = courseCode; }
public Integer getTeacherId() { return teacherId; }
public void setTeacherId(Integer teacherId) { this.teacherId = teacherId; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}

View File

@ -6,7 +6,6 @@ public class Student {
private String studentId; private String studentId;
private String class_; private String class_;
private String major; private String major;
private Integer userId;
public Integer getId() { public Integer getId() {
return id; return id;

View File

@ -1,95 +0,0 @@
package com.campus.entity;
import java.time.LocalDateTime;
public class Teacher {
private Integer id;
private String name;
private String teacherId;
private String department;
private String title;
private LocalDateTime createTime;
private LocalDateTime updateTime;
public Teacher() {
}
// 全参构造方法
public Teacher(Integer id, String name, String teacherId, String department, String title) {
this.id = id;
this.name = name;
this.teacherId = teacherId;
this.department = department;
this.title = title;
}
// Getter Setter 方法
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getTeacherId() {
return teacherId;
}
public void setTeacherId(String teacherId) {
this.teacherId = teacherId;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", name='" + name + '\'' +
", teacherId='" + teacherId + '\'' +
", department='" + department + '\'' +
", title='" + title + '\'' +
'}';
}
}

View File

@ -1,29 +1,41 @@
// com/campus/entity/User.java
package com.campus.entity; package com.campus.entity;
// User.java
public class User { public class User {
private Integer id; private Integer id;
private String username; private String username;
private String password; private String password;
private String role; // ADMIN/TEACHER/STUDENT private String role; // ADMIN/TEACHER/STUDENT
private Integer studentId; // 如果是学生角色关联学生ID
private Integer teacherId; // 如果是教师角色关联教师ID
// 新增构造方法
public User() {}
public User(String username, String password, String role) { public Integer getId() {
this.username = username; return id;
this.password = password;
this.role = role;
} }
// 已有的getter和setter方法... public void setId(Integer id) {
public Integer getId() { return id; } this.id = id;
public void setId(Integer id) { this.id = id; } }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; } public String getUsername() {
public String getPassword() { return password; } return username;
public void setPassword(String password) { this.password = password; } }
public String getRole() { return role; }
public void setRole(String role) { this.role = role; } public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
} }

View File

@ -1,61 +0,0 @@
// com/campus/interceptor/AuthInterceptor.java
package com.campus.interceptor;
import com.campus.entity.User;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.PrintWriter;
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String requestURI = request.getRequestURI();
// 公共接口不需要登录
if (requestURI.contains("/api/user/login") ||
requestURI.contains("/api/user/register") ||
requestURI.contains("/static/") ||
requestURI.startsWith("/error")) {
return true;
}
// API接口需要登录
if (requestURI.startsWith("/api/")) {
HttpSession session = request.getSession(false);
if (session == null || session.getAttribute("currentUser") == null) {
// 未登录返回JSON格式错误信息
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("{\"success\":false,\"message\":\"请先登录\",\"code\":401}");
out.flush();
return false;
}
// 权限验证简单版
User user = (User) session.getAttribute("currentUser");
String role = user.getRole();
// 学生只能访问特定接口
if ("STUDENT".equals(role)) {
if (!requestURI.contains("/api/attendance/my") &&
!requestURI.contains("/api/user/current") &&
!requestURI.contains("/api/user/logout")) {
response.setContentType("application/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write("{\"success\":false,\"message\":\"权限不足\",\"code\":403}");
out.flush();
return false;
}
}
}
return true;
}
}

View File

@ -1,38 +0,0 @@
package com.campus.mapper;
import com.campus.entity.AttendanceRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Mapper
public interface AttendanceMapper {
// 基础CRUD操作
int insert(AttendanceRecord record);
int update(AttendanceRecord record);
int delete(@Param("id") Integer id);
// 查询方法
AttendanceRecord selectById(@Param("id") Integer id);
List<AttendanceRecord> selectByStudentId(@Param("studentId") Integer studentId);
List<AttendanceRecord> selectByDate(@Param("date") Date date);
AttendanceRecord selectByStudentAndDate(@Param("studentId") Integer studentId,
@Param("date") Date date);
// 批量操作新增
int batchInsert(@Param("records") List<AttendanceRecord> records);
// 统计查询
List<Map<String, Object>> getAttendanceStatistics(@Param("startDate") Date startDate,
@Param("endDate") Date endDate);
Map<String, Object> getStudentAttendanceSummary(@Param("studentId") Integer studentId);
// 分页查询新增
List<AttendanceRecord> selectByCondition(@Param("record") AttendanceRecord condition,
@Param("offset") Integer offset,
@Param("limit") Integer limit);
}

View File

@ -2,23 +2,13 @@ package com.campus.mapper;
import com.campus.entity.Student; import com.campus.entity.Student;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List; import java.util.List;
@Mapper @Mapper
public interface StudentMapper { public interface StudentMapper {
// 基础CRUD
int insert(Student student);
int update(Student student);
int delete(@Param("id") Integer id);
// 查询方法
Student selectById(@Param("id") Integer id);
Student selectByStudentId(@Param("studentId") Integer studentId);
List<Student> selectAll(); List<Student> selectAll();
void insert(Student student);
// 条件查询新增 void update(Student student);
List<Student> selectByCondition(@Param("student") Student condition); void delete(Integer id);
List<Student> selectByIds(@Param("ids") List<Integer> ids);
} }

View File

@ -1,20 +0,0 @@
package com.campus.mapper;
import com.campus.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface UserMapper {
// 基础操作
int insert(User user);
int update(User user);
int delete(@Param("id") Long id);
// 查询方法
User selectById(@Param("id") Long id);
User selectByUsername(@Param("username") String username);
// 安全检查新增
User selectByUsernameForAuth(@Param("username") String username);
}

View File

@ -1,31 +0,0 @@
package com.campus.service;
import com.campus.entity.AttendanceRecord;
import java.util.Date;
import java.util.List;
import java.util.Map;
public interface AttendanceService {
// 记录考勤
boolean recordAttendance(AttendanceRecord record);
// 补签考勤
boolean makeUpAttendance(AttendanceRecord record);
// 查询学生考勤记录 - 参数类型改为 Integer
List<AttendanceRecord> getAttendanceByStudentId(Integer studentId);
// 查询日期考勤记录
List<AttendanceRecord> getAttendanceByDate(Date date);
// 查询统计信息
List<Map<String, Object>> getAttendanceStatistics(Date startDate, Date endDate);
// 查询学生考勤统计 - 参数类型改为 Integer
Map<String, Object> getStudentAttendanceSummary(Integer studentId);
// 检查是否已考勤 - 参数类型改为 Integer
boolean checkAttendanceExists(Integer studentId, Date date);
}

View File

@ -7,6 +7,7 @@ import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
// StudentService.java
@Service @Service
public class StudentService { public class StudentService {
@Autowired @Autowired
@ -19,17 +20,4 @@ public class StudentService {
public void add(Student student) { public void add(Student student) {
studentMapper.insert(student); studentMapper.insert(student);
} }
// 参数类型改为 Integer
public Student getByStudentId(Integer studentId) {
return studentMapper.selectByStudentId(studentId);
}
public void update(Student student) {
studentMapper.update(student);
}
public void delete(Integer id) {
studentMapper.delete(id);
}
} }

View File

@ -1,15 +0,0 @@
// com/campus/service/UserService.java
package com.campus.service;
import com.campus.entity.User;
public interface UserService {
// 登录
User login(String username, String password);
// 注册
boolean register(User user);
// 检查用户名是否已存在
boolean checkUsernameExists(String username);
}

View File

@ -1,99 +0,0 @@
package com.campus.service.impl;
import com.campus.entity.AttendanceRecord;
import com.campus.entity.Student;
import com.campus.mapper.AttendanceMapper;
import com.campus.mapper.StudentMapper;
import com.campus.service.AttendanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Service
public class AttendanceServiceImpl implements AttendanceService {
@Autowired
private AttendanceMapper attendanceMapper;
@Autowired
private StudentMapper studentMapper;
@Override
@Transactional
public boolean recordAttendance(AttendanceRecord record) {
// 验证学生是否存在 - 参数类型改为 Integer
Student student = studentMapper.selectByStudentId(record.getStudentId());
if (student == null) {
throw new RuntimeException("学生不存在,学号:" + record.getStudentId());
}
// 检查是否已考勤 - 参数类型改为 Integer
if (checkAttendanceExists(record.getStudentId(), record.getAttendanceDate())) {
throw new RuntimeException("该学生今天已考勤,学号:" + record.getStudentId());
}
// 设置创建时间
if (record.getAttendanceTime() == null) {
record.setAttendanceTime(new Date());
}
return attendanceMapper.insert(record) > 0;
}
@Override
@Transactional
public boolean makeUpAttendance(AttendanceRecord record) {
// 验证学生是否存在 - 参数类型改为 Integer
Student student = studentMapper.selectByStudentId(record.getStudentId());
if (student == null) {
throw new RuntimeException("学生不存在,学号:" + record.getStudentId());
}
// 检查是否已有记录 - 参数类型改为 Integer
AttendanceRecord existing = attendanceMapper.selectByStudentAndDate(
record.getStudentId(), record.getAttendanceDate());
if (existing != null) {
// 更新现有记录
existing.setStatus(record.getStatus());
existing.setRemarks(record.getRemarks());
existing.setAttendanceTime(new Date());
existing.setCreatedBy(record.getCreatedBy());
return attendanceMapper.update(existing) > 0;
} else {
// 创建新记录
return recordAttendance(record);
}
}
@Override
public List<AttendanceRecord> getAttendanceByStudentId(Integer studentId) { // 改为 Integer
return attendanceMapper.selectByStudentId(studentId);
}
@Override
public List<AttendanceRecord> getAttendanceByDate(Date date) {
return attendanceMapper.selectByDate(date);
}
@Override
public List<Map<String, Object>> getAttendanceStatistics(Date startDate, Date endDate) {
return attendanceMapper.getAttendanceStatistics(startDate, endDate);
}
@Override
public Map<String, Object> getStudentAttendanceSummary(Integer studentId) { // 改为 Integer
return attendanceMapper.getStudentAttendanceSummary(studentId);
}
@Override
public boolean checkAttendanceExists(Integer studentId, Date date) { // 改为 Integer
AttendanceRecord record = attendanceMapper.selectByStudentAndDate(studentId, date);
return record != null;
}
}

View File

@ -1,45 +0,0 @@
// com/campus/service/impl/UserServiceImpl.java
package com.campus.service.impl;
import com.campus.entity.User;
import com.campus.mapper.UserMapper;
import com.campus.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User login(String username, String password) {
// 1. 根据用户名查询用户
User user = userMapper.selectByUsername(username);
// 2. 直接比对明文密码作业简化版
if (user != null && password.equals(user.getPassword())) {
return user;
}
return null;
}
@Override
public boolean register(User user) {
// 检查用户名是否已存在
if (checkUsernameExists(user.getUsername())) {
return false;
}
// 密码直接存明文作业简化版
// 注意实际生产环境绝不允许这样做
userMapper.insert(user);
return true;
}
@Override
public boolean checkUsernameExists(String username) {
return userMapper.selectByUsername(username) != null;
}
// 完全删除原来的 encryptPassword 方法
}

View File

@ -1,119 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.campus.mapper.AttendanceMapper">
<resultMap id="attendanceMap" type="com.campus.entity.AttendanceRecord">
<id property="id" column="id"/>
<result property="studentId" column="student_id"/>
<result property="courseName" column="course_name"/>
<result property="attendanceDate" column="attendance_date"/>
<result property="attendanceTime" column="attendance_time"/>
<result property="status" column="status"/>
<result property="remarks" column="remarks"/>
<result property="createdBy" column="created_by"/>
<result property="createTime" column="create_time"/>
</resultMap>
<insert id="insert" parameterType="com.campus.entity.AttendanceRecord" useGeneratedKeys="true" keyProperty="id">
INSERT INTO attendance_record(student_id, course_name, attendance_date, attendance_time, status, remarks, created_by)
VALUES(#{studentId}, #{courseName}, #{attendanceDate}, #{attendanceTime}, #{status}, #{remarks}, #{createdBy})
</insert>
<!-- 修改参数类型为 integer -->
<select id="selectByStudentId" parameterType="integer" resultMap="attendanceMap">
SELECT * FROM attendance_record
WHERE student_id = #{studentId}
ORDER BY attendance_date DESC, attendance_time DESC
</select>
<select id="selectByDate" parameterType="date" resultMap="attendanceMap">
SELECT * FROM attendance_record
WHERE attendance_date = #{date}
ORDER BY student_id, attendance_time
</select>
<!-- 多参数方法不需要指定 parameterType -->
<select id="selectByStudentAndDate" resultMap="attendanceMap">
SELECT * FROM attendance_record
WHERE student_id = #{studentId} AND attendance_date = #{date}
</select>
<update id="update" parameterType="com.campus.entity.AttendanceRecord">
UPDATE attendance_record
SET status = #{status},
remarks = #{remarks},
attendance_time = #{attendanceTime},
created_by = #{createdBy}
WHERE id = #{id}
</update>
<delete id="delete" parameterType="int">
DELETE FROM attendance_record WHERE id = #{id}
</delete>
<!-- 新增selectById 方法 -->
<select id="selectById" parameterType="int" resultMap="attendanceMap">
SELECT * FROM attendance_record WHERE id = #{id}
</select>
<!-- 新增batchInsert 方法 -->
<insert id="batchInsert" parameterType="list">
INSERT INTO attendance_record(student_id, course_name, attendance_date, attendance_time, status, remarks, created_by)
VALUES
<foreach collection="records" item="record" separator=",">
(#{record.studentId}, #{record.courseName}, #{record.attendanceDate}, #{record.attendanceTime}, #{record.status}, #{record.remarks}, #{record.createdBy})
</foreach>
</insert>
<!-- 新增selectByCondition 方法 -->
<select id="selectByCondition" resultMap="attendanceMap">
SELECT * FROM attendance_record
<where>
<if test="record.studentId != null">
AND student_id = #{record.studentId}
</if>
<if test="record.courseName != null and record.courseName != ''">
AND course_name = #{record.courseName}
</if>
<if test="record.attendanceDate != null">
AND attendance_date = #{record.attendanceDate}
</if>
<if test="record.status != null and record.status != ''">
AND status = #{record.status}
</if>
</where>
ORDER BY attendance_date DESC, attendance_time DESC
<if test="limit != null and offset != null">
LIMIT #{offset}, #{limit}
</if>
</select>
<select id="getAttendanceStatistics" resultType="map">
SELECT
attendance_date as date,
COUNT(*) as total,
SUM(CASE WHEN status = 'PRESENT' THEN 1 ELSE 0 END) as present,
SUM(CASE WHEN status = 'ABSENT' THEN 1 ELSE 0 END) as absent,
SUM(CASE WHEN status = 'LATE' THEN 1 ELSE 0 END) as late
FROM attendance_record
WHERE attendance_date BETWEEN #{startDate} AND #{endDate}
GROUP BY attendance_date
ORDER BY attendance_date DESC
</select>
<!-- 这个方法的参数类型保持为 string -->
<select id="getStudentAttendanceSummary" parameterType="string" resultType="map">
SELECT
student_id,
COUNT(*) as totalCount,
SUM(CASE WHEN status = 'PRESENT' THEN 1 ELSE 0 END) as presentCount,
SUM(CASE WHEN status = 'ABSENT' THEN 1 ELSE 0 END) as absentCount,
SUM(CASE WHEN status = 'LATE' THEN 1 ELSE 0 END) as lateCount,
ROUND(SUM(CASE WHEN status = 'PRESENT' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as attendanceRate
FROM attendance_record
WHERE student_id = #{studentId}
</select>
</mapper>

View File

@ -3,39 +3,20 @@
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.campus.mapper.StudentMapper"> <mapper namespace="com.campus.mapper.StudentMapper">
<select id="selectAll" resultType="com.campus.entity.Student">
<resultMap id="studentMap" type="com.campus.entity.Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="studentId" column="student_id"/>
<result property="class_" column="class"/>
<result property="major" column="major"/>
</resultMap>
<select id="selectAll" resultMap="studentMap">
SELECT * FROM student SELECT * FROM student
</select> </select>
<!-- 修改 insert 和 update 语句中的 class 字段 -->
<select id="selectByStudentId" parameterType="string" resultMap="studentMap"> <insert id="insert" parameterType="com.campus.entity.Student">
SELECT * FROM student WHERE student_id = #{studentId} INSERT INTO student(name, student_id, `class`, major) <!-- 新增反引号 -->
</select>
<insert id="insert" parameterType="com.campus.entity.Student" useGeneratedKeys="true" keyProperty="id">
INSERT INTO student(name, student_id, `class`, major)
VALUES(#{name}, #{studentId}, #{class_}, #{major}) VALUES(#{name}, #{studentId}, #{class_}, #{major})
</insert> </insert>
<update id="update" parameterType="com.campus.entity.Student"> <update id="update" parameterType="com.campus.entity.Student">
UPDATE student UPDATE student
SET name = #{name}, SET name = #{name}, student_id = #{studentId}, `class` = #{class_}, major = #{major} <!-- 新增反引号 -->
student_id = #{studentId},
`class` = #{class_},
major = #{major}
WHERE id = #{id} WHERE id = #{id}
</update> </update>
<delete id="delete" parameterType="java.lang.Integer"> <delete id="delete" parameterType="java.lang.Integer">
DELETE FROM student WHERE id = #{id} DELETE FROM student WHERE id = #{id}
</delete> </delete>
</mapper> </mapper>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.campus.mapper.UserMapper">
<resultMap id="userMap" type="com.campus.entity.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="role" column="role"/>
</resultMap>
<select id="selectByUsername" parameterType="string" resultMap="userMap">
SELECT * FROM user WHERE username = #{username}
</select>
<insert id="insert" parameterType="com.campus.entity.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO user(username, password, role)
VALUES(#{username}, #{password}, #{role})
</insert>
</mapper>

View File

@ -13,6 +13,7 @@
<!-- 扫描 Controller --> <!-- 扫描 Controller -->
<context:component-scan base-package="com.campus.controller"/> <context:component-scan base-package="com.campus.controller"/>
<!-- 视图解析器 --> <!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/> <property name="prefix" value="/WEB-INF/jsp/"/>
@ -26,26 +27,4 @@
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters> </mvc:message-converters>
</mvc:annotation-driven> </mvc:annotation-driven>
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/api/**"/>
<bean class="com.campus.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
<value>application/json;charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
</beans> </beans>

View File

@ -1,112 +0,0 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>管理员主页</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #333; color: white; padding: 10px; display: flex; justify-content: space-between; }
.menu { margin: 20px 0; }
.menu a { margin-right: 20px; text-decoration: none; color: #333; padding: 5px 10px; border: 1px solid #ccc; }
.menu a:hover { background-color: #f0f0f0; }
.content { margin-top: 20px; }
table { width: 100%; border-collapse: collapse; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background-color: #f0f0f0; }
</style>
</head>
<body>
<div class="header">
<h2>学生考勤系统 - 管理员</h2>
<div>
欢迎,${user.username}(管理员) |
<a href="javascript:void(0)" onclick="logout()">退出</a>
</div>
</div>
<div class="menu">
<a href="javascript:void(0)" onclick="loadContent('users')">用户管理</a>
<a href="javascript:void(0)" onclick="loadContent('students')">学生管理</a>
<a href="javascript:void(0)" onclick="loadContent('attendance')">考勤统计</a>
</div>
<div id="content" class="content">
<h3>欢迎使用学生考勤管理系统</h3>
<p>请选择左侧菜单进行操作</p>
</div>
<script>
// 添加补零函数,兼容旧环境
function padZero(num) {
return num < 10 ? '0' + num : num;
}
function loadContent(type) {
if (type === 'students') {
$.get("/student/list", function(data) {
let html = '<h3>学生列表</h3><table><tr><th>ID</th><th>学号</th><th>姓名</th><th>班级</th><th>专业</th></tr>';
data.forEach(s => {
html += `<tr>
<td>${s.id}</td>
<td>${s.studentId}</td>
<td>${s.name}</td>
<td>${s.class_}</td>
<td>${s.major}</td>
</tr>`;
});
html += '</table>';
$("#content").html(html);
});
} else if (type === 'attendance') {
$.get("/api/attendance/statistics", function(response) {
if (response.success) {
let html = '<h3>考勤统计</h3>';
if (response.statistics && response.statistics.length > 0) {
html += '<table><tr><th>日期</th><th>总人数</th><th>到课</th><th>缺勤</th><th>迟到</th><th>到课率</th></tr>';
response.statistics.forEach(stat => {
// 使用兼容的方式格式化日期
const date = new Date(stat.date);
const year = date.getFullYear();
const month = padZero(date.getMonth() + 1); // 月份从0开始需要+1
const day = padZero(date.getDate());
const formattedDate = `${year}-${month}-${day}`;
const rate = (stat.present * 100 / stat.total).toFixed(2);
html += `<tr>
<td>${formattedDate}</td>
<td>${stat.total}</td>
<td>${stat.present}</td>
<td>${stat.absent}</td>
<td>${stat.late}</td>
<td>${rate}%</td>
</tr>`;
});
html += '</table>';
} else {
html += '<p>暂无考勤数据</p>';
}
$("#content").html(html);
}
});
} else {
$("#content").html("<h3>用户管理</h3><p>用户管理功能开发中...</p>");
}
}
function logout() {
$.post("/api/user/logout", function() {
window.location.href = "/login";
});
}
// 页面加载时检查登录状态
$(function() {
$.get("/api/user/current", function(response) {
if (!response.success) {
window.location.href = "/login";
}
});
});
</script>
</body>
</html>

View File

@ -1,83 +0,0 @@
<!-- WEB-INF/jsp/login.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录 - 学生考勤系统</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
.container { max-width: 400px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input[type="text"], input[type="password"] { width: 100%; padding: 8px; box-sizing: border-box; }
button { width: 100%; padding: 10px; background-color: #4CAF50; color: white; border: none; cursor: pointer; }
.error { color: red; margin-top: 10px; }
.success { color: green; margin-top: 10px; }
</style>
</head>
<body>
<div class="container">
<h2>学生考勤系统登录</h2>
<div class="form-group">
<label>用户名:</label>
<input type="text" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" id="password" placeholder="请输入密码">
</div>
<button onclick="login()">登录</button>
<button onclick="goToRegister()" style="margin-top: 10px; background-color: #2196F3;">注册</button>
<div id="message" class="error"></div>
</div>
<script>
function login() {
const username = $("#username").val();
const password = $("#password").val();
if (!username || !password) {
$("#message").text("用户名和密码不能为空").addClass("error").removeClass("success");
return;
}
$.ajax({
url: "/campus_attendance_war/api/user/login",
type: "POST",
contentType: "application/json",
data: JSON.stringify({username: username, password: password}),
success: function(response) {
if (response.success) {
$("#message").text("登录成功,正在跳转...").addClass("success").removeClass("error");
// 根据角色跳转到不同页面
if (response.role === "ADMIN") {
window.location.href = "/campus_attendance_war/admin";
} else if (response.role === "TEACHER") {
window.location.href = "/campus_attendance_war/teacher";
} else {
window.location.href = "/campus_attendance_war/student";
}
} else {
$("#message").text(response.message).addClass("error").removeClass("success");
}
},
error: function() {
$("#message").text("登录失败,请检查网络").addClass("error").removeClass("success");
}
});
}
function goToRegister() {
// 使用上下文路径拼接正确的注册页面映射路径
window.location.href = "${pageContext.request.contextPath}/register";
}
// 回车键登录
$("#password").keypress(function(e) {
if (e.which == 13) {
login();
}
});
</script>
</body>
</html>

View File

@ -1,102 +0,0 @@
<!-- WEB-INF/jsp/register.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>注册 - 学生考勤系统</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 50px; }
.container { max-width: 400px; margin: 0 auto; padding: 20px; border: 1px solid #ccc; border-radius: 5px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; }
input[type="text"], input[type="password"], select { width: 100%; padding: 8px; box-sizing: border-box; }
button { width: 100%; padding: 10px; background-color: #2196F3; color: white; border: none; cursor: pointer; }
.error { color: red; margin-top: 10px; }
.success { color: green; margin-top: 10px; }
</style>
</head>
<body>
<div class="container">
<h2>用户注册</h2>
<div class="form-group">
<label>用户名:</label>
<input type="text" id="username" placeholder="请输入用户名">
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" id="password" placeholder="请输入密码至少6位">
</div>
<div class="form-group">
<label>确认密码:</label>
<input type="password" id="confirmPassword" placeholder="请再次输入密码">
</div>
<div class="form-group">
<label>角色:</label>
<select id="role">
<option value="STUDENT">学生</option>
<option value="TEACHER">教师</option>
<option value="ADMIN">管理员</option>
</select>
</div>
<button onclick="register()">注册</button>
<button onclick="goToLogin()" style="margin-top: 10px; background-color: #4CAF50;">返回登录</button>
<div id="message" class="error"></div>
</div>
<script>
function register() {
const username = $("#username").val();
const password = $("#password").val();
const confirmPassword = $("#confirmPassword").val();
const role = $("#role").val();
// 简单验证
if (!username || username.length < 3) {
$("#message").text("用户名至少3位").addClass("error").removeClass("success");
return;
}
if (!password || password.length < 6) {
$("#message").text("密码至少6位").addClass("error").removeClass("success");
return;
}
if (password !== confirmPassword) {
$("#message").text("两次密码不一致").addClass("error").removeClass("success");
return;
}
$.ajax({
// 正确写法:拼接上下文路径
url: "${pageContext.request.contextPath}/api/user/register",
type: "POST",
contentType: "application/json",
data: JSON.stringify({
username: username,
password: password,
role: role
}),
success: function(response) {
if (response.success) {
$("#message").text("注册成功,请登录").addClass("success").removeClass("error");
// 注册成功后跳转登录页(正确写法)
setTimeout(function() {
window.location.href = "${pageContext.request.contextPath}/login";
}, 2000);
} else {
$("#message").text(response.message).addClass("error").removeClass("success");
}
},
error: function() {
$("#message").text("注册失败,请检查网络").addClass("error").removeClass("success");
}
});
}
function goToLogin() {
window.location.href = "${pageContext.request.contextPath}/login";
}
</script>
</body>
</html>

View File

@ -1,140 +0,0 @@
<!-- WEB-INF/jsp/student.jsp -->
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>学生主页</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/dayjs@1.10.7/dayjs.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #4CAF50; color: white; padding: 10px; display: flex; justify-content: space-between; }
.content { margin-top: 20px; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background-color: #f0f0f0; }
.status-present { color: green; }
.status-absent { color: red; }
.status-late { color: orange; }
.summary { background-color: #f9f9f9; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
.summary-item { display: inline-block; margin-right: 30px; }
</style>
</head>
<body>
<div class="header">
<h2>学生考勤系统 - 学生</h2>
<div>
欢迎,${user.username}(学生) |
<a href="javascript:void(0)" onclick="logout()">退出</a>
</div>
</div>
<div class="content">
<div class="summary">
<h3>我的考勤统计</h3>
<div id="summaryStats">
<p>加载中...</p>
</div>
</div>
<h3>我的考勤记录</h3>
<div id="attendanceRecords">
<p>加载中...</p>
</div>
</div>
<script>
$(function() {
loadMyAttendance();
loadStatistics();
});
function loadMyAttendance() {
$.get("api/attendance/my", function(response) {
if (response.success) {
let html = '';
if (response.records && response.records.length > 0) {
html += '<table><tr><th>日期</th><th>课程</th><th>时间</th><th>状态</th><th>备注</th><th>记录人</th></tr>';
response.records.forEach(record => {
const statusClass = 'status-' + record.status.toLowerCase();
const date = new Date(record.attendanceDate);
const time = record.attendanceTime ? new Date(record.attendanceTime).toLocaleTimeString() : '';
html += `<tr>
<td>${date.toLocaleDateString()}</td>
<td>${record.courseName}</td>
<td>${time}</td>
<td class="${statusClass}">${record.status}</td>
<td>${record.remarks || ''}</td>
<td>${record.createdBy}</td>
</tr>`;
});
html += '</table>';
} else {
html = '<p>暂无考勤记录</p>';
}
$("#attendanceRecords").html(html);
} else {
$("#attendanceRecords").html(`<p class="error">${response.message}</p>`);
}
});
}
function loadStatistics() {
// 注意:这里需要知道当前学生的学号
$.get("api/user/current", function(userResponse) {
if (userResponse.success) {
const username = userResponse.user.username;
// 请求学生考勤统计
$.get(`api/attendance/statistics?studentId=${username}`, function(response) {
if (response.success && response.summary) {
const summary = response.summary;
// 修复使用正确的属性名根据你的AttendanceMapper.xml中的别名
// 注意这里使用的是你SQL查询中定义的别名
const totalCount = summary.totalCount || summary.totalcount || 0;
const presentCount = summary.presentCount || summary.presentcount || 0;
const absentCount = summary.absentCount || summary.absentcount || 0;
const lateCount = summary.lateCount || summary.latecount || 0;
const attendanceRate = summary.attendanceRate || summary.attendancerate || 0;
let html = `
<div class="summary-item">
<h4>总次数</h4>
<p>${totalCount}</p>
</div>
<div class="summary-item">
<h4>到课</h4>
<p class="status-present">${presentCount}</p>
</div>
<div class="summary-item">
<h4>缺勤</h4>
<p class="status-absent">${absentCount}</p>
</div>
<div class="summary-item">
<h4>迟到</h4>
<p class="status-late">${lateCount}</p>
</div>
<div class="summary-item">
<h4>到课率</h4>
<p>${attendanceRate}%</p>
</div>`;
$("#summaryStats").html(html);
} else {
$("#summaryStats").html('<p>暂无统计信息</p>');
}
});
}
});
}
function logout() {
$.post("api/user/logout", function() {
window.location.href = "login";
});
}
</script>
</body>
</html>

View File

@ -1,609 +0,0 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>教师主页 - 学生考勤系统</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.header { background-color: #2196F3; color: white; padding: 10px; display: flex; justify-content: space-between; }
.menu { margin: 20px 0; }
.menu a { margin-right: 20px; text-decoration: none; color: #333; padding: 5px 10px; border: 1px solid #ccc; }
.menu a:hover { background-color: #f0f0f0; }
.content { margin-top: 20px; }
table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
th, td { border: 1px solid #ccc; padding: 8px; text-align: left; }
th { background-color: #f0f0f0; }
.form-group { margin-bottom: 10px; }
.form-group label { display: inline-block; width: 100px; }
.status-present { color: green; }
.status-absent { color: red; }
.status-late { color: orange; }
.btn { padding: 8px 15px; background-color: #4CAF50; color: white; border: none; cursor: pointer; margin: 5px; }
.btn-blue { background-color: #2196F3; }
.btn-red { background-color: #f44336; }
.message { padding: 10px; margin: 10px 0; border-radius: 4px; }
.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.info { background-color: #d1ecf1; color: #0c5460; border: 1px solid #bee5eb; }
</style>
</head>
<body>
<div class="header">
<h2>学生考勤系统 - 教师</h2>
<div>
欢迎,${user.username}(教师) |
<a href="javascript:void(0)" onclick="logout()" style="color:white; text-decoration:underline;">退出</a>
</div>
</div>
<div class="menu">
<a href="javascript:void(0)" onclick="loadContent('record')">📝 记录考勤</a>
<a href="javascript:void(0)" onclick="loadContent('query')">🔍 查询考勤</a>
<a href="javascript:void(0)" onclick="loadContent('makeup')">✏️ 补签管理</a>
<a href="javascript:void(0)" onclick="loadContent('statistics')">📊 考勤统计</a>
<a href="javascript:void(0)" onclick="loadContent('students')">👨‍🎓 学生列表</a>
</div>
<div id="content" class="content">
<h3>欢迎使用学生考勤系统</h3>
<p>请选择上方菜单进行操作</p>
<div id="messageArea"></div>
</div>
<script>
// ==================== 辅助函数 ====================
// 获取今天的日期字符串 (YYYY-MM-DD)
function getTodayDateString() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// 格式化后端返回的日期
function formatDate(dateValue) {
if (!dateValue) return '未知日期';
try {
const date = new Date(dateValue);
return date.toLocaleDateString('zh-CN');
} catch (e) {
return dateValue;
}
}
// 显示消息
function showMessage(message, type = 'info') {
const messageArea = $("#messageArea");
messageArea.html(`<div class="message ${type}">${message}</div>`);
if (type !== 'info') {
setTimeout(() => messageArea.empty(), 5000);
}
}
// ==================== 主功能函数 ====================
function loadContent(type) {
$("#messageArea").empty(); // 清空消息区域
if (type === 'record') {
loadRecordAttendance();
} else if (type === 'query') {
loadQueryAttendance();
} else if (type === 'makeup') {
loadMakeupAttendance();
} else if (type === 'statistics') {
loadStatistics();
} else if (type === 'students') {
loadStudentList();
}
}
// 1. 记录考勤
function loadRecordAttendance() {
$.get("/campus_attendance_war/student/list", function(students) {
if (!students || students.length === 0) {
showMessage("没有找到学生数据", "error");
return;
}
let html = `
<h3>📝 记录考勤</h3>
<div class="form-group">
<label>课程名称:</label>
<input type="text" id="courseName" value="高等数学" style="width:200px;">
</div>
<div class="form-group">
<label>考勤日期:</label>
<input type="date" id="attendanceDate" style="width:200px;">
</div>
<div class="form-group">
<button class="btn" onclick="selectAllStudents()">全选</button>
<button class="btn" onclick="deselectAllStudents()">取消全选</button>
<button class="btn btn-blue" onclick="setAllStatus('PRESENT')">全部到课</button>
<button class="btn btn-red" onclick="setAllStatus('ABSENT')">全部缺勤</button>
</div>
<table>
<tr>
<th>选择</th>
<th>学号</th>
<th>姓名</th>
<th>班级</th>
<th>状态</th>
<th>备注</th>
</tr>`;
students.forEach(s => {
html += `<tr>
<td><input type="checkbox" class="student-check" data-id="${s.studentId}"></td>
<td>${s.studentId}</td>
<td>${s.name}</td>
<td>${s.class_}</td>
<td>
<select class="status-select">
<option value="PRESENT">到课</option>
<option value="LATE">迟到</option>
<option value="ABSENT">缺勤</option>
</select>
</td>
<td><input type="text" class="remarks" placeholder="备注" style="width:150px;"></td>
</tr>`;
});
html += `</table>
<div class="form-group">
<button class="btn" onclick="submitAttendance()">提交考勤</button>
<button class="btn" onclick="clearForm()">清空</button>
</div>
<div id="submitResult"></div>`;
$("#content").html(html);
$("#attendanceDate").val(getTodayDateString());
showMessage("请选择学生并设置考勤状态", "info");
}).fail(function() {
showMessage("加载学生列表失败", "error");
});
}
// 记录考勤辅助函数
function selectAllStudents() {
$(".student-check").prop('checked', true);
}
function deselectAllStudents() {
$(".student-check").prop('checked', false);
}
function setAllStatus(status) {
$(".status-select").val(status);
}
function clearForm() {
$(".student-check").prop('checked', false);
$(".status-select").val('PRESENT');
$(".remarks").val('');
$("#courseName").val('高等数学');
$("#attendanceDate").val(getTodayDateString());
}
function submitAttendance() {
const courseName = $("#courseName").val().trim();
const attendanceDate = $("#attendanceDate").val();
if (!courseName) {
showMessage("请输入课程名称", "error");
return;
}
if (!attendanceDate) {
showMessage("请选择考勤日期", "error");
return;
}
const records = [];
$(".student-check:checked").each(function() {
const row = $(this).closest('tr');
const studentId = row.find('td').eq(1).text();
const status = row.find('.status-select').val();
const remarks = row.find('.remarks').val().trim();
records.push({
studentId: studentId,
courseName: courseName,
attendanceDate: new Date(attendanceDate).getTime(),
status: status,
remarks: remarks || ''
});
});
if (records.length === 0) {
showMessage("请至少选择一名学生", "error");
return;
}
showMessage(`正在提交 ${records.length} 条考勤记录...`, "info");
// 逐条提交(更稳定)
let successCount = 0;
let failCount = 0;
let processed = 0;
function submitNext() {
if (processed >= records.length) {
const result = `
<div class="message success">
<h4>提交完成!</h4>
<p>成功: ${successCount}条 | 失败: ${failCount}条</p>
</div>`;
$("#submitResult").html(result);
return;
}
const record = records[processed];
$.ajax({
url: '/campus_attendance_war/api/attendance/record',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(record),
success: function(response) {
if (response.success) {
successCount++;
} else {
failCount++;
}
processed++;
submitNext();
},
error: function() {
failCount++;
processed++;
submitNext();
}
});
}
submitNext();
}
// 2. 查询考勤
function loadQueryAttendance() {
let html = `
<h3>🔍 查询考勤</h3>
<div class="form-group">
<label>查询方式:</label>
<select id="queryType" onchange="changeQueryType()">
<option value="date">按日期查询</option>
<option value="student">按学生查询</option>
<option value="course">按课程查询</option>
</select>
</div>
<div id="queryParams"></div>
<div class="form-group">
<button class="btn" onclick="queryAttendance()">查询</button>
<button class="btn" onclick="clearQuery()">清空</button>
</div>
<div id="queryResult"></div>`;
$("#content").html(html);
changeQueryType();
}
function changeQueryType() {
const type = $("#queryType").val();
let html = '';
if (type === 'date') {
html = `
<div class="form-group">
<label>查询日期:</label>
<input type="date" id="queryDate">
</div>`;
} else if (type === 'student') {
html = `
<div class="form-group">
<label>学生学号:</label>
<input type="text" id="queryStudentId" placeholder="输入学号">
</div>`;
} else if (type === 'course') {
html = `
<div class="form-group">
<label>课程名称:</label>
<input type="text" id="queryCourseName" placeholder="输入课程名称">
</div>`;
}
$("#queryParams").html(html);
if (type === 'date') {
$("#queryDate").val(getTodayDateString());
}
}
function clearQuery() {
$("#queryParams input").val('');
$("#queryResult").empty();
}
function queryAttendance() {
const type = $("#queryType").val();
let url = '/campus_attendance_war/api/attendance/query?';
if (type === 'date') {
const date = $("#queryDate").val();
if (!date) {
showMessage("请选择查询日期", "error");
return;
}
url += `date=${date}`;
} else if (type === 'student') {
const studentId = $("#queryStudentId").val().trim();
if (!studentId) {
showMessage("请输入学号", "error");
return;
}
url += `studentId=${studentId}`;
} else if (type === 'course') {
const courseName = $("#queryCourseName").val().trim();
if (!courseName) {
showMessage("请输入课程名称", "error");
return;
}
// 注意:这里需要后端支持按课程查询,暂时用日期查询代替
url += `date=${getTodayDateString()}`;
}
showMessage("正在查询...", "info");
$.get(url, function(response) {
if (response.success) {
let html = '<h4>查询结果</h4>';
if (response.records && response.records.length > 0) {
html += `<p>共找到 ${response.records.length} 条记录</p>`;
html += '<table><tr><th>学号</th><th>课程</th><th>日期</th><th>时间</th><th>状态</th><th>备注</th><th>记录人</th></tr>';
response.records.forEach(record => {
const statusClass = 'status-' + record.status.toLowerCase();
const dateStr = formatDate(record.attendanceDate);
const timeStr = record.attendanceTime ? formatDate(record.attendanceTime).split(' ')[1] || '' : '';
html += `<tr>
<td>${record.studentId}</td>
<td>${record.courseName}</td>
<td>${dateStr}</td>
<td>${timeStr}</td>
<td class="${statusClass}">${record.status}</td>
<td>${record.remarks || ''}</td>
<td>${record.createdBy || '系统'}</td>
</tr>`;
});
html += '</table>';
} else {
html += '<div class="message info">暂无考勤记录</div>';
}
$("#queryResult").html(html);
} else {
$("#queryResult").html(`<div class="message error">${response.message || "查询失败"}</div>`);
}
}).fail(function() {
$("#queryResult").html('<div class="message error">查询失败,请检查网络连接</div>');
});
}
// 3. 补签管理
function loadMakeupAttendance() {
let html = `
<h3>✏️ 补签管理</h3>
<div class="form-group">
<label>学生学号:</label>
<input type="text" id="makeupStudentId" placeholder="输入学号" style="width:200px;">
</div>
<div class="form-group">
<label>课程名称:</label>
<input type="text" id="makeupCourse" placeholder="输入课程名称" style="width:200px;">
</div>
<div class="form-group">
<label>补签日期:</label>
<input type="date" id="makeupDate" style="width:200px;">
</div>
<div class="form-group">
<label>考勤状态:</label>
<select id="makeupStatus" style="width:200px;">
<option value="PRESENT">到课</option>
<option value="LATE">迟到</option>
<option value="ABSENT">缺勤</option>
</select>
</div>
<div class="form-group">
<label>备注:</label>
<input type="text" id="makeupRemarks" placeholder="补签原因" style="width:300px;">
</div>
<div class="form-group">
<button class="btn" onclick="submitMakeup()">提交补签</button>
<button class="btn" onclick="clearMakeupForm()">清空</button>
</div>
<div id="makeupResult"></div>`;
$("#content").html(html);
$("#makeupDate").val(getTodayDateString());
showMessage("请填写补签信息", "info");
}
function clearMakeupForm() {
$("#makeupStudentId").val('');
$("#makeupCourse").val('');
$("#makeupRemarks").val('');
$("#makeupDate").val(getTodayDateString());
$("#makeupStatus").val('PRESENT');
$("#makeupResult").empty();
}
function submitMakeup() {
const studentId = $("#makeupStudentId").val().trim();
const courseName = $("#makeupCourse").val().trim();
const date = $("#makeupDate").val();
const status = $("#makeupStatus").val();
const remarks = $("#makeupRemarks").val().trim();
if (!studentId || !courseName || !date) {
showMessage("请填写完整信息(学号、课程、日期必填)", "error");
return;
}
showMessage("正在提交补签...", "info");
$.ajax({
url: '/campus_attendance_war/api/attendance/makeup',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({
studentId: studentId,
courseName: courseName,
attendanceDate: new Date(date).getTime(),
status: status,
remarks: remarks || '教师补签'
}),
success: function(response) {
if (response.success) {
$("#makeupResult").html('<div class="message success">补签成功!</div>');
clearMakeupForm();
} else {
$("#makeupResult").html(`<div class="message error">${response.message || "补签失败"}</div>`);
}
},
error: function() {
$("#makeupResult").html('<div class="message error">提交失败,请检查网络连接</div>');
}
});
}
// 4. 考勤统计
function loadStatistics() {
showMessage("正在加载统计信息...", "info");
// 获取最近7天的统计
const endDate = getTodayDateString();
const startDate = new Date();
startDate.setDate(startDate.getDate() - 7);
const startDateStr = startDate.toISOString().split('T')[0];
$.get(`/campus_attendance_war/api/attendance/statistics?startDate=${startDateStr}&endDate=${endDate}`, function(response) {
let html = '<h3>📊 考勤统计最近7天</h3>';
if (response.success && response.statistics && response.statistics.length > 0) {
// 计算总计
let totalCount = 0, presentCount = 0, absentCount = 0, lateCount = 0;
html += '<table><tr><th>日期</th><th>总人数</th><th>到课</th><th>缺勤</th><th>迟到</th><th>到课率</th></tr>';
response.statistics.forEach(stat => {
const total = stat.total || stat.totalcount || 0;
const present = stat.present || stat.presentcount || 0;
const absent = stat.absent || stat.absentcount || 0;
const late = stat.late || stat.latecount || 0;
const rate = total > 0 ? ((present * 100) / total).toFixed(2) : 0;
totalCount += total;
presentCount += present;
absentCount += absent;
lateCount += late;
const dateStr = formatDate(stat.date);
html += `<tr>
<td>${dateStr}</td>
<td>${total}</td>
<td class="status-present">${present}</td>
<td class="status-absent">${absent}</td>
<td class="status-late">${late}</td>
<td>${rate}%</td>
</tr>`;
});
// 总计行
const totalRate = totalCount > 0 ? ((presentCount * 100) / totalCount).toFixed(2) : 0;
html += `<tr style="font-weight:bold; background-color:#f0f0f0;">
<td>总计</td>
<td>${totalCount}</td>
<td class="status-present">${presentCount}</td>
<td class="status-absent">${absentCount}</td>
<td class="status-late">${lateCount}</td>
<td>${totalRate}%</td>
</tr>`;
html += '</table>';
// 统计摘要
html += `
<div class="message info" style="margin-top:20px;">
<h4>统计摘要</h4>
<p>总考勤人次: ${totalCount} | 到课率: ${totalRate}%</p>
<p>到课: ${presentCount} (${totalCount > 0 ? ((presentCount*100)/totalCount).toFixed(1) : 0}%)</p>
<p>缺勤: ${absentCount} (${totalCount > 0 ? ((absentCount*100)/totalCount).toFixed(1) : 0}%)</p>
<p>迟到: ${lateCount} (${totalCount > 0 ? ((lateCount*100)/totalCount).toFixed(1) : 0}%)</p>
</div>`;
} else {
html += '<div class="message info">暂无考勤统计数据</div>';
}
$("#content").html(html);
}).fail(function() {
$("#content").html('<div class="message error">加载统计信息失败</div>');
});
}
// 5. 学生列表
function loadStudentList() {
$.get("/campus_attendance_war/student/list", function(students) {
let html = '<h3>👨‍🎓 学生列表</h3>';
if (students && students.length > 0) {
html += `<p>共 ${students.length} 名学生</p>`;
html += '<table><tr><th>学号</th><th>姓名</th><th>班级</th><th>专业</th></tr>';
students.forEach(s => {
html += `<tr>
<td>${s.studentId}</td>
<td>${s.name}</td>
<td>${s.class_}</td>
<td>${s.major}</td>
</tr>`;
});
html += '</table>';
} else {
html += '<div class="message info">暂无学生数据</div>';
}
$("#content").html(html);
}).fail(function() {
$("#content").html('<div class="message error">加载学生列表失败</div>');
});
}
// ==================== 系统函数 ====================
function logout() {
if (confirm("确定要退出登录吗?")) {
$.post("/campus_attendance_war/api/user/logout", function() {
window.location.href = "/campus_attendance_war/login";
});
}
}
// 页面加载时检查登录状态
$(function() {
// 修正登录状态检查纯EL表达式不在JS中混用
<c:if test="${user == null}">
window.location.href = "/campus_attendance_war/login";
return;
</c:if>
// 修正欢迎信息提取直接使用EL表达式传递用户名
showMessage(`欢迎 ${'${user.username}'} 老师!`, "success");
// 自动加载学生列表
loadStudentList();
});
</script>
</body>
</html>