news 2026/10/2 22:53:04

职工考勤管理系统:从数据库设计到状态判定完整实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
职工考勤管理系统:从数据库设计到状态判定完整实战

简介:数据库课程设计——职工考勤管理信息系统完整设计文档,面向计算机相关专业学生及需要完成数据库课程设计的人员。文档以企业考勤管理为背景,系统阐述从需求分析、概念结构设计到逻辑结构设计、物理结构设计与数据库实施的完整流程,覆盖员工信息管理、考勤记录、考勤统计、权限管理、异常处理等功能模块,并通过数据流图、功能模块图、系统数据流程图梳理系统结构。资源为单个doc格式文档,压缩包大小316KB,正文包含局部与整体ER图、关系模式、数据关系图、存储记录结构设计、索引创建、数据库与数据表建立、存储过程和触发器创建等具体内容,目录章节划分清晰,可作为课程设计报告撰写和数据库方案设计的直接参考。目前已有67人学习下载,适合需要完成类似考勤管理系统数据库设计或理解系统化设计流程的读者。

1. 数据库课程设计里最容易被低估的题目:职工考勤管理信息系统

拿到“职工考勤管理信息系统”这个课程设计题目,多数人的第一反应是“不就是一张员工表加一张打卡表,做几个增删改查页面嘛”。真做完一遍的人会知道,这个题目表面是管理信息系统,实际考的是你对外键策略、时间状态演算、跨天班次和异常数据的态度——而这些恰恰是答辩时老师最愿意追问的地方。网上流传的“推荐文档”大多只给了一份格式模板,能把表结构设计讲明白的很少;本文就从一张考勤业务的数据骨架开始,把从建表到跑通演示的完整路径拆开讲。适合正在做数据库课程设计的学生,以及需要一个可快速落地版本参考的开发者。

2. 需求梳理与数据建模:先画出考勤业务的数据骨架

2.1 考勤系统必须覆盖的四类业务对象:员工、部门、班次、打卡记录

在做职工考勤管理信息系统之前,先别急着打开 Navicat 建表。课程设计的评分标准里,数据库设计通常占三到四成,而设计的起点是需求分析。考勤业务的核心对象只有四类:员工、部门、班次、打卡记录。部门提供组织归属,员工是考勤主体,班次定义了上下班时间规则,打卡记录是每天产生的原始事实。再往后延伸,请假、加班、补卡都是围绕这四个对象派生出来的子业务。

把对象列出来后,要画一张 ER 图再动手建表。很多学生图省事直接建表,结果写到后面发现员工和部门之间的关系没表达清楚,或者考勤记录里缺少“今天是哪个班次”的信息。ER 图不需要画得多专业,只要把实体、属性和关系标注清楚就行。常见做法是员工到部门是多对一,员工到考勤记录是一对多,班次到考勤记录是一对多。这个环节大约花半小时,能省掉后面三天改表结构的时间。

需要注意的是,考勤记录表里应该保存的是“打卡这个动作发生的时间点”,而不是直接保存“迟到”“早退”“正常”这样的判断结果。原始事实和业务判断要分开存,这是整个考勤系统设计的核心原则。判断结果可以被程序算出来,但原始打卡时间一旦丢失或覆盖,后面任何规则调整都会让你痛不欲生。

2.2 从 ER 图到表结构:五个核心表和三个边界问题

一个能支撑课程设计答辩的考勤系统,至少要有五张核心表:部门表、员工表、班次表、考勤记录表、请假表。加班的场景如果要做,再补一张加班申请表。部门表和员工表是主数据,班次表是规则数据,考勤记录表是事实数据,请假表是例外数据。

设计表结构时会碰到三个边界问题,这三个问题在答辩时被问到的概率极高。

第一个是“一个人一天打很多次卡怎么处理”。考勤机不会帮你只留下上班和下班两条记录,实际情况是员工可能打了四次、五次,甚至漏打一次。处理方案有两种:一种是用程序取当天第一条作为上班打卡、最后一条作为下班打卡;另一种是在考勤记录表里用 clock_in_time 和 clock_out_time 两个字段,服务端把同一天的多次打卡合并成一条记录。课程设计规模下,第二种更直观,报表也好写。

第二个是“跨天班次怎么处理”。夜班晚上十点上班、次日早上六点下班,这条记录该算在哪一天?很多人的做法是直接取日期字段,结果夜班的下班打卡永远对不上。正确做法是引入“名义日期”概念,以班次的开始日期作为归属日。这个细节放到第四章详细讲,这里先记住结论。

第三个是“请假和考勤怎么联动”。请假记录通过员工ID关联到员工,在统计出勤时要做一次排除:当天有请假记录的,不参与迟到早退判断。不要在考勤记录表里加一堆“是否请假”的冗余字段,通过员工ID和日期关联查询才是干净的模型。

2.3 字段级别的取舍:用单一状态字段还是用两个时间字段

这是考勤表设计里最常见的纠结:考勤记录表到底用 clock_in_time、clock_out_time 两个字段,还是用 one_time 一个字段加多条记录,再加一个 type 字段区分上班还是下班?

我一般会选两个时间字段的方案。理由有两点。第一,报表查询简单,统计每天的上下班时间只需要一行记录;如果用多条记录方案,每次统计都要做聚合,SQL 写起来长,答辩时老师还会追问聚合逻辑的边界情况。第二,两个时间字段天然对应“上班打卡”和“下班打卡”两个动作,缺哪个就是漏卡,程序判断非常直接。

代价是:同一个人打三次卡的时候,服务端需要做一次合并或丢弃。常见处理是保留最早的作为上班时间、最晚的作为下班时间,中间那次忽略。这个逻辑放在程序里,而不是放在数据库里,数据库只负责存事实。

字段类型的选择也要注意。打卡时间用 DATETIME,不要用 VARCHAR,否则排序和比较会出问题。状态类字段,比如考勤状态,用 TINYINT 或者 VARCHAR 加 CHECK 约束都可以,但值一定要用英文或数字,不要用中文。后面第五章会专门讲为什么状态值别用中文。

3. MySQL 建表脚本与索引设计:可直接抄的落地版本

3.1 建库建表 SQL:五张表的完整脚本与字段说明

如果你所在学校的课程设计环境是 MySQL 5.7 或 8.0,下面这套脚本可以直接拿去用。字符集统一用 utf8mb4,排序规则用 utf8mb4_general_ci,这是避免中文乱码的第一道保险。

CREATE DATABASE IF NOT EXISTS attendance_system DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE attendance_system; CREATE TABLE dept ( dept_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '部门ID', dept_name VARCHAR(50) NOT NULL COMMENT '部门名称', manager_id INT NULL COMMENT '部门负责人员工ID' ) ENGINE=InnoDB COMMENT='部门表'; CREATE TABLE employee ( emp_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID', emp_no VARCHAR(20) NOT NULL UNIQUE COMMENT '工号', emp_name VARCHAR(30) NOT NULL COMMENT '姓名', gender TINYINT NOT NULL DEFAULT 1 COMMENT '性别: 1男 2女', dept_id INT NOT NULL COMMENT '所属部门ID', hire_date DATE NOT NULL COMMENT '入职日期', is_active TINYINT NOT NULL DEFAULT 1 COMMENT '在职状态: 1在职 0离职', KEY idx_dept_id (dept_id) ) ENGINE=InnoDB COMMENT='员工表'; CREATE TABLE shift ( shift_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '班次ID', shift_name VARCHAR(30) NOT NULL COMMENT '班次名称', start_time TIME NOT NULL COMMENT '上班时间', end_time TIME NOT NULL COMMENT '下班时间', late_tolerance INT NOT NULL DEFAULT 0 COMMENT '迟到容忍分钟数' ) ENGINE=InnoDB COMMENT='班次表'; CREATE TABLE attendance ( att_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '考勤记录ID', emp_id INT NOT NULL COMMENT '员工ID', work_date DATE NOT NULL COMMENT '名义日期', shift_id INT NOT NULL COMMENT '班次ID', clock_in_time DATETIME NULL COMMENT '上班打卡时间', clock_out_time DATETIME NULL COMMENT '下班打卡时间', att_status TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0未判定 1正常 2迟到 3早退 4旷工 5漏卡', UNIQUE KEY uk_emp_date (emp_id, work_date), KEY idx_att_status (att_status), KEY idx_shift_id (shift_id) ) ENGINE=InnoDB COMMENT='考勤记录表'; CREATE TABLE leave_record ( leave_id INT PRIMARY KEY AUTO_INCREMENT COMMENT '请假ID', emp_id INT NOT NULL COMMENT '员工ID', leave_date DATE NOT NULL COMMENT '请假日期', leave_type TINYINT NOT NULL COMMENT '类型: 1事假 2病假 3年假', reason VARCHAR(200) NULL COMMENT '请假原因' ) ENGINE=InnoDB COMMENT='请假记录表';

这套脚本里,attendance 表是核心。work_date 字段存的是名义日期,不是打卡动作实际发生的日期——这一点放在第四章跨天班次里展开。UNIQUE KEY uk_emp_date 约束了员工和日期的唯一性,防止同一天产生两条考勤记录。clock_in_time 和 clock_out_time 都允许为 NULL,NULL 就代表漏卡,这个设计比用默认值‘0000-00-00’干净得多。

运行环境上的注意点:MySQL 8.0 默认的驱动是 com.mysql.cj.jdbc.Driver,不是旧版的 com.mysql.jdbc.Driver。如果你的代码里还在写后者,连接会直接报类找不到或驱动版本不兼容。建表前先确认数据库服务端字符集已经是 utf8mb4,否则 INSERT 中文时出现 Incorrect string value 错误,根源在库的默认配置。

3.2 把外键和索引放在刀刃上:哪些表加外键、哪些不加

课程设计里最常见的翻车操作,是给每张表都加上 FOREIGN KEY,摆出一副“我很规范”的姿态。结果删除一条部门数据时,被员工表的外键约束拦住,报错信息又看不懂,最后只能手动一条条删。这不是外键的错,是使用场景没分清。

我建表的原则是:开发期和课程设计演示期,外键能不加就不加,用普通索引加应用层判断来替代。原因很实际——MySQL 的外键约束在删除和更新时会触发额外的检查,学生写的删除逻辑往往没有考虑级联策略,导致系统里到处是“删除失败”的弹窗。改成普通索引后,程序里先查关联记录、再决定是否执行删除,逻辑完全可控,答辩时还能多讲一句“我在应用层做了关联检查”。

但索引要加到位。employee 表的 dept_id 加普通索引,因为部门查询员工是高频操作。attendance 表的 emp_id、work_date 加联合唯一索引,这是考勤查询最核心的组合条件。leave_record 的 emp_id 和 leave_date 也加索引,统计某员工某月考勤时,请假排除查询会快很多。索引不是越多越好,每张表三到五个就够,建多了反而拖慢 INSERT 速度。

外键真正适合的场景是底层数据几乎不变、删除靠级联的表关系。比如班次表 shift 被 attendance 引用时,如果班次被删会导致历史考勤失去规则依据,所以要么用外键禁止删除,要么在应用层判断“该班次存在考勤记录时不允许删除”。课程设计阶段,应用层判断足够了。

3.3 初始化测试数据:让系统第一次打开就有东西可看

课程设计验收时,老师打开系统,最怕看到空荡荡的表格页面。空表意味着你的程序可能根本没有正常写入过数据,也看不出查询逻辑对不对。正确的做法是准备一份初始化脚本,让系统装上就能看到覆盖四类场景的数据:正常出勤、迟到、漏卡、请假。

INSERT INTO dept (dept_id, dept_name) VALUES (1, '技术部'), (2, '人事部'); INSERT INTO employee (emp_no, emp_name, gender, dept_id, hire_date) VALUES ('E001', '张三', 1, 1, '2022-03-01'), ('E002', '李四', 2, 2, '2023-01-15'); INSERT INTO shift (shift_id, shift_name, start_time, end_time, late_tolerance) VALUES (1, '白班', '09:00:00', '18:00:00', 10), (2, '夜班', '22:00:00', '06:00:00', 10); INSERT INTO attendance (emp_id, work_date, shift_id, clock_in_time, clock_out_time) VALUES (1, '2024-05-06', 1, '2024-05-06 08:55:00', '2024-05-06 18:05:00'), (1, '2024-05-07', 1, '2024-05-07 09:20:00', '2024-05-07 18:10:00'), (2, '2024-05-06', 1, '2024-05-06 09:02:00', NULL), (2, '2024-05-07', 1, NULL, NULL); INSERT INTO leave_record (emp_id, leave_date, leave_type, reason) VALUES (2, '2024-05-08', 2, '感冒发烧');

数据量不用大,但场景要全。张三在 5 月 6 日是正常出勤,5 月 7 日是迟到;李四在 5 月 6 日是下班漏卡,5 月 7 日是全天未打卡,5 月 8 日请假。这套数据配合第四章的状态判定逻辑,程序跑起来后每一条都有对应的业务含义,答辩演示时满屏都是可讲的内容。

注意:插入考勤记录时先不要填 att_status,保持默认值 0。状态字段交给程序去更新,而不是在 SQL 里手工写死。这样能证明你的系统有真实的判定逻辑,而不是拿手工数据充数。

4. 考勤业务逻辑实现:从打卡记录到出勤状态的演算

4.1 为什么不能把“迟到”“正常”直接存成数据库字段

有相当一部分课程设计会把 attendance 表设计成直接存“正常”“迟到”“早退”“旷工”这样的文本状态。表面上看起来没问题,查询也直观,但老师只要追问一句“如果公司把迟到容忍时间从 10 分钟改成 5 分钟,你怎么改”,你就得把所有历史记录全部 UPDATE 一遍。更麻烦的是,如果班次规则本身发生了变化,旧数据的判断口径和新数据不一致,报表统计就是一笔糊涂账。

正确做法是:数据库只存打卡的原始时间戳和班次 ID,状态由程序在查询或写入时动态计算。这样改迟到容忍时间,只需要改班次表里的 late_tolerance 字段,历史数据的统计口径自动跟着新规则走。这也是“数据库存事实、程序算结论”这个原则的最典型体现。

实现上可以有两个方案。方案一是在每次打卡后立刻调用判定函数,把结果写回 att_status 字段,好处是查询时不需要现场计算,坏处是如果班次规则变了,历史状态就失真了,需要跑一次批量重算。方案二是不写状态字段,每次查询时由后台服务根据打卡时间和班次规则现场计算,好处是口径永远一致,坏处是查询性能略受影响。课程设计规模下,我建议用方案一,因为它直观好讲,而且可以在系统里做一个“重新计算当月考勤”的按钮来弥补规则变更问题,这个按钮本身就是答辩亮点。

4.2 状态判定算法:迟到、早退、旷工、漏卡的四类判定逻辑

下面是一段可以直接拷进后端服务的 Python 判定函数。输入是班次对象和打卡时间,输出是状态码。注释里说明了每个分支对应的业务含义。

from datetime import datetime, time def judge_attendance(shift, work_date, clock_in, clock_out): """ 判定单日考勤状态 :param shift: 包含 start_time, end_time, late_tolerance 的班次对象 :param work_date: 名义日期,date 类型,归属到上班那一天 :param clock_in: 上班打卡时间,datetime 或 None :param clock_out: 下班打卡时间,datetime 或 None :return: 状态码 int """ # 全天没有任何打卡:旷工 if clock_in is None and clock_out is None: return 4 # 只打了上班卡,没有下班卡:下班漏卡 if clock_in is not None and clock_out is None: return 5 # 只打了下班卡,没有上班卡:上班漏卡 if clock_in is None and clock_out is not None: return 5 # 用名义日期+班次时间构造标准上下班时间点 # 注意:这里要考虑跨天班次,end_time 小于 start_time 时加班一天 shift_start = datetime.combine(work_date, shift.start_time) if shift.end_time > shift.start_time: shift_end = datetime.combine(work_date, shift.end_time) else: shift_end = datetime.combine(work_date, shift.end_time) + timedelta(days=1) # 迟到:上班打卡时间晚于(标准上班时间 + 容忍分钟数) if clock_in > shift_start + timedelta(minutes=shift.late_tolerance): return 2 # 早退:下班打卡时间早于标准下班时间,且早退超过10分钟 if clock_out < shift_end - timedelta(minutes=10): return 3 # 正常 return 1

这段逻辑里最关键的参数是 late_tolerance 和早退容差。迟到容忍是班次表里的配置,比如白班 9 点上班、容忍 10 分钟,那么 9:10 之前打卡都算正常,9:10:01 之后就算迟到。早退容差我没有做成表字段,直接在代码里写死为 10 分钟——如果你要做得更规范,可以像 late_tolerance 一样把早退容忍也加进班次表。

一个容易被忽略的坑:判断迟到时用的是“上班时间 + 容忍分钟数”,而不是直接用上班时间。如果直接把 9:00 当作判定线,员工 9:09 打卡会被判迟到,但业务上容忍 10 分钟,这就是规则和实现不一致。把容忍逻辑写进程序后,班次表调参就能生效,这也呼应了前面说的“规则变更不需要改历史数据”。

4.3 跨天班次的处理逻辑:把日期时间戳归一化成名义日期

跨天班次是考勤系统里最容易翻车的地方。夜班 22:00 上班、次日 06:00 下班,如果直接按打卡日期的自然日来分组,5 月 6 日晚上 22:00 的上班打卡会被归到 5 月 6 日,而 5 月 7 日早上 06:00 的下班打卡会被归到 5 月 7 日,一条完整的夜班记录被拆成了两天,统计全乱。

解决办法是引入“名义日期”(work_date)概念。规则是:以班次开始上班的那一天作为该班次的归属日期。夜班 5 月 6 日 22:00 开始,那么这条考勤记录的 work_date 就是 5 月 6 日,下班打卡时间即便实际发生在 5 月 7 日凌晨,依然挂在 5 月 6 日这条记录下。

落到代码上,就是前面判定函数里的这段逻辑:当班次 end_time 小于 start_time 时,计算标准下班时间要在名义日期上加一天。这行判断就解决了跨天问题。还有一个前置步骤:后台服务在把打卡记录写入考勤表时,要判断打卡时间的小时数来判断它属于白班还是夜班。常见做法是:如果打卡时间点离某个班次开始时间在 5 小时内,就归到该班次,否则尝试匹配其他班次。课程设计里班次通常只有两三个,这个匹配逻辑用循环就能解决。

答辩时如果老师问“夜班的考勤怎么算”,你能把名义日期的设计讲清楚,这一个小点就能把分数拉开一档。

5. 课程设计避坑清单:从建表到答辩最容易翻车的六个地方

5.1 MySQL 8.0 连接报错:时区导致的服务端拒绝连接

现象:程序启动时抛The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized,或者干脆连不上数据库。原因:MySQL 8.0 默认时区配置和 JDBC 驱动不匹配,驱动要求明确指定时区。解决:在 JDBC 连接串后面追加serverTimezone=Asia/Shanghai&useSSL=false,或者在 MySQL 里执行SET GLOBAL time_zone = '+8:00'。对于驱动类名,8.0 必须用com.mysql.cj.jdbc.Driver,别再抄旧教程里的com.mysql.jdbc.Driver。这个问题一度被很多学生当成玄学,实际上是一个参数的事。

5.2 中文写入报错 Incorrect string value:建库时没定字符集

现象:INSERT 中文姓名或部门名时报错,但英文数据正常。原因:数据库或表的默认字符集是 latin1,不支持中文。解决:建库时用DEFAULT CHARACTER SET utf8mb4,连接串里加characterEncoding=utf8。注意查看表结构用SHOW CREATE TABLE employee;,确认CHARSET=utf8mb4再继续。如果表已经建好,用ALTER TABLE employee CONVERT TO CHARACTER SET utf8mb4;补救。这里还要提醒一句:字段名、表名不要用中文,哪怕数据库支持也别用,后面写 SQL 时引号、编码问题会把你耗死。

5.3 删除部门时外键报错:误用外键约束导致删不掉

现象:删除一条部门记录时提示Cannot delete or update a parent row: a foreign key constraint fails。原因:部门表被员工表通过外键引用,直接删除违反正则约束。解决:常见做法是取消物理外键,改用普通索引加应用层逻辑;如果坚持用外键,删除前先检查员工表里是否存在该部门的在职员工,存在则提示“该部门下还有员工,无法删除”。课程设计里我更推荐后者,因为代码里可以完整展示这个关联检查逻辑,答辩时是加分项。

5.4 明明打了卡但状态全是旷工:日期比较时忽略了跨天

现象:夜班员工的下班打卡时间在凌晨,状态被判定为旷工或漏卡。原因:程序直接用自然日分组,没有按名义日期归并。解决:按照第四章 4.3 的做法,work_date 以班次开始时间所在日期为准;判定函数里遇到 end_time 小于 start_time 的情况,标准下班时间自动加一天。验证方法:找到一条夜班记录,打印出 shift_start 和 shift_end 两个变量,确认结束时间比开始时间晚。

5.5 考勤报表统计结果对不上:只统计了有打卡记录的人

现象:报表里出勤人数永远小于员工总数,部分员工整月没有记录。原因:SQL 查询用内连接,从考勤记录表出发,没有打卡记录的员工自然不会被查出来。解决:统计时应从 employee 表左连接 attendance 表,用LEFT JOIN,然后再用条件筛选,把没有打卡记录的员工归为旷工。这里有一个固定写法:先LEFT JOIN,再在 SELECT 里用CASE WHEN把无记录的行转成“旷工”状态。

5.6 演示时数据库连接池爆掉:反复开关连接,页面卡死

现象:系统点击几次查询后开始卡顿,最后提示Too many connections。原因:代码里每次查询都新建连接,用完没有关闭;或者连接池配置太小。解决:如果用的是 JDBC,记得在 finally 里关闭 Connection、Statement、ResultSet。如果用了连接池,比如 HikariCP,把 maximumPoolSize 设为 10 到 20,在课程设计演示环境下完全足够。这个现象在答辩现场很常见,因为演示时会反复点击页面,连接不释放就是死路一条。这里也算是“数据库连接池”这类配置的一次实际应用,理解参数含义比背配置重要。

6. 跑通最小可运行的 Flask + SQLite 版本:答辩前最后的实战演练

6.1 一个文件跑起的后端骨架:表定义、路由与数据库增删改查

如果你的课程设计允许使用 Python 技术栈,我强烈推荐用 Flask + SQLite 做最小演示版本。SQLite 不需要安装数据库服务端,文件在哪程序就在哪,答辩现场不会出现 MySQL 连不上的尴尬。下面的代码是一个单文件后端,包含了部门查询、员工列表、打卡记录插入和考勤状态重算四个核心功能。

import sqlite3 from flask import Flask, request, jsonify from datetime import datetime, date, timedelta app = Flask(__name__) DB_PATH = "attendance.db" def get_db(): conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): conn = get_db() conn.executescript(""" CREATE TABLE IF NOT EXISTS employee ( emp_id INTEGER PRIMARY KEY AUTOINCREMENT, emp_no TEXT NOT NULL UNIQUE, emp_name TEXT NOT NULL, dept_id INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS attendance ( att_id INTEGER PRIMARY KEY AUTOINCREMENT, emp_id INTEGER NOT NULL, work_date TEXT NOT NULL, clock_in TEXT, clock_out TEXT, att_status INTEGER DEFAULT 0, UNIQUE(emp_id, work_date) ); """) conn.commit() conn.close() @app.route("/employee/list") def employee_list(): conn = get_db() rows = conn.execute("SELECT * FROM employee").fetchall() conn.close() return jsonify([dict(r) for r in rows]) @app.route("/attendance/checkin", methods=["POST"]) def checkin(): data = request.get_json() emp_id = data["emp_id"] now = datetime.now() work_date = now.date().isoformat() conn = get_db() conn.execute(""" INSERT INTO attendance (emp_id, work_date, clock_in) VALUES (?, ?, ?) ON CONFLICT(emp_id, work_date) DO UPDATE SET clock_in = COALESCE(attendance.clock_in, excluded.clock_in) """, (emp_id, work_date, now.isoformat())) conn.commit() conn.close() return jsonify({"code": 0, "msg": "打卡成功"}) @app.route("/attendance/recalc") def recalc(): conn = get_db() rows = conn.execute("SELECT * FROM attendance").fetchall() for row in rows: status = judge_simple(row["clock_in"], row["clock_out"]) conn.execute("UPDATE attendance SET att_status=? WHERE att_id=?", (status, row["att_id"])) conn.commit() conn.close() return jsonify({"code": 0, "msg": "重算完成"}) def judge_simple(clock_in, clock_out): # 简化判定:假定白班 09:00-18:00,迟到线 09:10 if not clock_in: return 4 if not clock_out else 5 if not clock_out: return 5 in_time = datetime.fromisoformat(clock_in) if in_time.time() > datetime.strptime("09:10:00", "%H:%M:%S").time(): return 2 return 1 if __name__ == "__main__": init_db() app.run(host="127.0.0.1", port=5000, debug=True)

这个文件的三个路由分别对应课程设计里的三个核心功能:查询员工属于数据库增删改查里的 R(Retrieve),打卡录入是 C(Create),重算状态是 U(Update)。SQLite 和 MySQL 的语法差异主要体现在自增主键和 ON CONFLICT 处理上,如果你开发时用 MySQL、演示时改用 SQLite,上面代码里的表结构和写法可以直接平移。记住:课程设计的重点是数据库设计思路和业务逻辑,不是数据库服务器的安装配置。

6.2 把 SQLite 用于演示的取舍:什么可以省略、什么不能省略

如果你在开发阶段用的是 MySQL,答辩前想转到 SQLite 做演示,需要注意三点。第一,表结构调整要重新执行,因为 SQLite 不支持直接修改列定义,你的 five 张核心表要重新 CREATE 一遍。第二,SQL 函数有差异,比如 MySQL 的 NOW() 在 SQLite 里是 datetime('now', 'localtime'),需要逐一替换。第三,外键默认是关闭的,SQLite 里要执行PRAGMA foreign_keys = ON;才会启用。

我的建议是:开发环境直接用 SQLite,不要切换。它的并发能力足够支撑课程设计演示;而且文件型数据库让学生更容易理解数据持久化的概念——关闭程序再打开,数据还在。答辩评委如果要看表结构,直接连上文件就能查,不需要输账号密码。这套方案在“环境装不上导致系统跑不起来”这类翻车场景上,能帮你兜住最后的底线。

最后说一个我自己的习惯:每次交课程设计之前,一定会把数据库文件删掉,从零跑一遍初始化脚本,再手动通过页面做一次打卡、查询、重算的全流程,确认换一台电脑也能复现。很多同学开发时一切正常,答辩时换了机器、数据库没导入或者密码不对,当场黑屏。这类问题和技术水平无关,纯粹是交付习惯。如果你照着本文把建表脚本、判定函数和最小后端跑通一遍,再自己走一遍全流程,这个题目基本就稳了。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 22:51:55

Embedding与LLM输出向量是什么关系?Java后端RAG实战解析

Java 后端最近一年面试&#xff0c;AI 相关的问题肉眼可见地变多了。我帮朋友做模拟面试时&#xff0c;最常被问到的一个题就是标题里这个&#xff1a;Embedding 和 LLM 输出向量到底啥关系&#xff1f;大部分候选人第一反应都是“都是向量&#xff0c;差不多吧”。这句话也不能…

作者头像 李华
网站建设 2026/10/2 22:50:39

TerraScan点云处理实战:参数原理与LiDAR测绘精度控制

简介&#xff1a;本资源是一份面向测绘、遥感、地理信息系统&#xff08;GIS&#xff09;及三维建模领域从业者与高校相关专业师生的技术参考文献&#xff0c;系统讲解基于TerraScan软件的LiDAR点云数据处理全流程。内容涵盖LiDAR技术原理与发展现状、TerraScan核心功能&#x…

作者头像 李华
网站建设 2026/10/2 22:49:02

让SOP从墙上走进系统:装配工位AI视频分析质量管控实践

这几年我跑过不少装配车间&#xff0c;最深的感触是&#xff1a;大多数工厂不是没有SOP&#xff0c;而是SOP和实际作业之间隔着一层“眼不见为净”。墙上挂着标准作业流程图&#xff0c;工位上贴着装配要点&#xff0c;但真到了节拍紧张的时候&#xff0c;工人怎么做、有没有跳…

作者头像 李华
网站建设 2026/10/2 22:48:23

OpenClaw本地AI代理部署实战:架构拆解与Qwen2.5模型接入

1. 从一条热搜说起&#xff1a;OpenClaw到底在解决什么问题 第一次在GitHub趋势榜上刷到OpenClaw这个项目时&#xff0c;我的反应和大多数人一样——又是一个"AI代理框架"&#xff1f;这两年打着Agent旗号的项目没有一千也有八百&#xff0c;多数是套壳GPT加几个工具…

作者头像 李华
网站建设 2026/10/2 22:42:55

cron表达式详解:从秒级到小时级,定时任务写法一次搞懂

cron表达式真是个神奇的东西&#xff0c;看起来就几个星号加斜杠&#xff0c;真要写对却没几个同事能一次搞定。尤其是“每N秒”“每N分钟”“每N小时”这种高频需求&#xff0c;网上答案五花八门&#xff0c;抄错了也不知道问题出在哪。我翻了翻手头的项目&#xff0c;发现大部…

作者头像 李华