Compare commits
7 Commits
2ba0e5c6a7
...
dcb5e9d61a
| Author | SHA1 | Date | |
|---|---|---|---|
| dcb5e9d61a | |||
| 8f3d32219b | |||
|
|
6089c25662 | ||
|
|
5feda21fc8 | ||
|
|
e9dfa6570d | ||
|
|
6454f5291f | ||
|
|
4f8e7f4d6e |
@ -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">{}</component>
|
<component name="KubernetesApiProvider"><![CDATA[{}]]></component>
|
||||||
<component name="MavenProjectsManager">
|
<component name="MavenProjectsManager">
|
||||||
<option name="originalFiles">
|
<option name="originalFiles">
|
||||||
<list>
|
<list>
|
||||||
|
|||||||
102
pom.xml
102
pom.xml
@ -6,22 +6,28 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<groupId>com.campus</groupId>
|
<groupId>com.campus</groupId>
|
||||||
<artifactId>campus-attendance</artifactId>
|
<artifactId>student-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>17</maven.compiler.source>
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<maven.compiler.target>17</maven.compiler.target>
|
<maven.compiler.target>1.8</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.0.7</mybatis.spring.version> <!-- 修正后的行 -->
|
<mybatis.spring.version>2.1.1</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 -->
|
<!-- Spring Framework -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
<artifactId>spring-context</artifactId>
|
<artifactId>spring-context</artifactId>
|
||||||
@ -37,6 +43,11 @@
|
|||||||
<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>
|
||||||
@ -50,73 +61,86 @@
|
|||||||
<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>8.0.33</version>
|
<version>${mysql.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba</groupId>
|
<groupId>com.alibaba</groupId>
|
||||||
<artifactId>druid</artifactId>
|
<artifactId>druid</artifactId>
|
||||||
<version>1.2.15</version>
|
<version>${druid.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- Servlet API -->
|
<!-- Servlet & JSP -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>javax.servlet</groupId>
|
<groupId>javax.servlet</groupId>
|
||||||
<artifactId>javax.servlet-api</artifactId>
|
<artifactId>javax.servlet-api</artifactId>
|
||||||
<version>4.0.1</version>
|
<version>${servlet.version}</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 -->
|
<!-- JSON Processing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
<version>2.14.1</version>
|
<version>${jackson.version}</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>campus-attendance</finalName>
|
<finalName>student-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>
|
||||||
<source>1.8</source>
|
<failOnMissingWebXml>false</failOnMissingWebXml>
|
||||||
<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>
|
||||||
|
|||||||
199
readme.md
199
readme.md
@ -1,35 +1,184 @@
|
|||||||
MySQL中创建数据库和表:
|
--用户表 (user)
|
||||||
|
|
||||||
sql
|
|
||||||
-- 创建数据库
|
|
||||||
CREATE DATABASE campus_attendance DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
||||||
USE campus_attendance;
|
|
||||||
|
|
||||||
-- 创建学生表
|
|
||||||
CREATE TABLE student (
|
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
||||||
name VARCHAR(50) NOT NULL,
|
|
||||||
student_id VARCHAR(20) NOT NULL UNIQUE,
|
|
||||||
class VARCHAR(50),
|
|
||||||
major VARCHAR(100)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 创建用户表
|
|
||||||
CREATE TABLE user (
|
CREATE TABLE user (
|
||||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
username VARCHAR(50) NOT NULL UNIQUE,
|
username VARCHAR(50) UNIQUE NOT NULL,
|
||||||
password VARCHAR(100) NOT NULL,
|
password VARCHAR(100) NOT NULL,
|
||||||
role VARCHAR(20) 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)
|
||||||
INSERT INTO student (name, student_id, class, major) VALUES
|
CREATE TABLE student (
|
||||||
('张三', '2023001', '计算机1班', '计算机科学与技术'),
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
('李四', '2023002', '软件1班', '软件工程');
|
user_id INT UNIQUE NOT NULL,
|
||||||
|
name VARCHAR(50) NOT NULL,
|
||||||
|
student_id VARCHAR(20) UNIQUE NOT NULL,
|
||||||
|
class_name VARCHAR(50),
|
||||||
|
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 (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
course_name VARCHAR(100) NOT NULL,
|
||||||
|
course_code VARCHAR(20) UNIQUE NOT NULL,
|
||||||
|
teacher_id INT 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 (
|
||||||
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
student_id INT NOT NULL,
|
||||||
|
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
|
||||||
|
(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');
|
||||||
206
src/main/java/com/campus/controller/AttendanceController.java
Normal file
206
src/main/java/com/campus/controller/AttendanceController.java
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他方法保持不变...
|
||||||
|
}
|
||||||
@ -1,8 +1,13 @@
|
|||||||
|
// 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 {
|
||||||
|
|
||||||
@ -15,4 +20,44 @@ 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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
// com/campus/controller/StudentController.java
|
||||||
package com.campus.controller;
|
package com.campus.controller;
|
||||||
|
|
||||||
import com.campus.entity.Student;
|
import com.campus.entity.Student;
|
||||||
@ -5,9 +6,10 @@ 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 {
|
||||||
@ -19,9 +21,67 @@ 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 String add(@RequestBody Student student) {
|
public Map<String, Object> add(@RequestBody Student student) {
|
||||||
studentService.add(student);
|
Map<String, Object> result = new HashMap<>();
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
141
src/main/java/com/campus/controller/UserController.java
Normal file
141
src/main/java/com/campus/controller/UserController.java
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/main/java/com/campus/entity/AttendanceRecord.java
Normal file
50
src/main/java/com/campus/entity/AttendanceRecord.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// 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; }
|
||||||
|
|
||||||
|
}
|
||||||
26
src/main/java/com/campus/entity/Course.java
Normal file
26
src/main/java/com/campus/entity/Course.java
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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; }
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ 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;
|
||||||
|
|||||||
95
src/main/java/com/campus/entity/Teacher.java
Normal file
95
src/main/java/com/campus/entity/Teacher.java
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
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 + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,41 +1,29 @@
|
|||||||
|
// 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 Integer getId() {
|
public User(String username, String password, String role) {
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Integer id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUsername(String username) {
|
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
|
||||||
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPassword(String password) {
|
|
||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
|
||||||
|
|
||||||
public String getRole() {
|
|
||||||
return role;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRole(String role) {
|
|
||||||
this.role = role;
|
this.role = role;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 已有的getter和setter方法...
|
||||||
|
public Integer getId() { return id; }
|
||||||
|
public void setId(Integer id) { this.id = id; }
|
||||||
|
public String getUsername() { return username; }
|
||||||
|
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; }
|
||||||
}
|
}
|
||||||
61
src/main/java/com/campus/interceptor/AuthInterceptor.java
Normal file
61
src/main/java/com/campus/interceptor/AuthInterceptor.java
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/main/java/com/campus/mapper/AttendanceMapper.java
Normal file
38
src/main/java/com/campus/mapper/AttendanceMapper.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@ -2,13 +2,23 @@ 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);
|
// 条件查询(新增)
|
||||||
void delete(Integer id);
|
List<Student> selectByCondition(@Param("student") Student condition);
|
||||||
|
List<Student> selectByIds(@Param("ids") List<Integer> ids);
|
||||||
}
|
}
|
||||||
20
src/main/java/com/campus/mapper/UserMapper.java
Normal file
20
src/main/java/com/campus/mapper/UserMapper.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
31
src/main/java/com/campus/service/AttendanceService.java
Normal file
31
src/main/java/com/campus/service/AttendanceService.java
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
@ -7,7 +7,6 @@ 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
|
||||||
@ -20,4 +19,17 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
15
src/main/java/com/campus/service/UserService.java
Normal file
15
src/main/java/com/campus/service/UserService.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// 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);
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/main/java/com/campus/service/impl/UserServiceImpl.java
Normal file
45
src/main/java/com/campus/service/impl/UserServiceImpl.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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 方法
|
||||||
|
}
|
||||||
119
src/main/resources/mapper/AttendanceMapper.xml
Normal file
119
src/main/resources/mapper/AttendanceMapper.xml
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
<?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>
|
||||||
@ -3,20 +3,39 @@
|
|||||||
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 字段 -->
|
|
||||||
<insert id="insert" parameterType="com.campus.entity.Student">
|
<select id="selectByStudentId" parameterType="string" resultMap="studentMap">
|
||||||
INSERT INTO student(name, student_id, `class`, major) <!-- 新增反引号 -->
|
SELECT * FROM student WHERE student_id = #{studentId}
|
||||||
|
</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}, student_id = #{studentId}, `class` = #{class_}, major = #{major} <!-- 新增反引号 -->
|
SET name = #{name},
|
||||||
|
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>
|
||||||
23
src/main/resources/mapper/UserMapper.xml
Normal file
23
src/main/resources/mapper/UserMapper.xml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?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>
|
||||||
@ -13,7 +13,6 @@
|
|||||||
<!-- 扫描 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/"/>
|
||||||
@ -27,4 +26,26 @@
|
|||||||
<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>
|
||||||
112
src/main/webapp/WEB-INF/jsp/admin.jsp
Normal file
112
src/main/webapp/WEB-INF/jsp/admin.jsp
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<%@ 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>
|
||||||
83
src/main/webapp/WEB-INF/jsp/login.jsp
Normal file
83
src/main/webapp/WEB-INF/jsp/login.jsp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<!-- 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>
|
||||||
102
src/main/webapp/WEB-INF/jsp/register.jsp
Normal file
102
src/main/webapp/WEB-INF/jsp/register.jsp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<!-- 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>
|
||||||
140
src/main/webapp/WEB-INF/jsp/student.jsp
Normal file
140
src/main/webapp/WEB-INF/jsp/student.jsp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<!-- 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>
|
||||||
609
src/main/webapp/WEB-INF/jsp/teacher.jsp
Normal file
609
src/main/webapp/WEB-INF/jsp/teacher.jsp
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
<%@ 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>
|
||||||
Loading…
Reference in New Issue
Block a user