【免费】人脸识别 智能考勤系统(深度学习+OpenCV DNN+FastAPI+Vue3) 锋哥原创出品,必属精品

📅 2026/8/6 17:21:50
【免费】人脸识别 智能考勤系统(深度学习+OpenCV DNN+FastAPI+Vue3) 锋哥原创出品,必属精品
大家好我是Java1234_小锋老师分享一套锋哥原创的人脸识别 智能考勤系统(深度学习OpenCV DNNFastAPIVue3)项目介绍随着深度学习与计算机视觉技术的快速发展人脸识别逐渐成为身份认证与智能考勤领域的重要手段。传统考勤方式普遍存在代打卡、效率低、统计不便等问题难以满足现代企事业单位精细化管理的需要。本文围绕“带人脸识别的智能考勤系统设计与实现”这一课题设计并实现了一套前后端分离的智能考勤系统。系统后端采用 Python 语言与 FastAPI 框架构建 RESTful 接口服务使用 SQLAlchemy 访问 MySQL 数据库前端采用 Vue3、Element Plus、Pinia、Axios 与 ECharts 实现管理后台与刷脸打卡页面人脸识别核心基于 OpenCV DNN 模块结合 YuNet 人脸检测模型与 SFace 人脸识别模型完成人脸定位、对齐、128 维特征提取与余弦相似度比对。系统实现了管理员登录与个人中心、部门岗位员工班次管理、人脸库注册、刷脸签到签退、考勤状态自动判定、请假加班审批、识别日志查询以及首页数据统计等功能。测试结果表明系统运行稳定识别流程清晰能够有效提升考勤管理效率具有较好的实用价值与推广意义。源码下载链接: https://pan.baidu.com/s/1xjca762MfAu-4zCuoG7jrQ?pwd1234提取码: 1234系统展示核心代码考勤业务服务打卡判定与统计。 from datetime import date, datetime, timedelta from decimal import Decimal from typing import Optional from sqlalchemy import func from sqlalchemy.orm import Session from app.models.attendance import Attendance from app.models.department import Department from app.models.employee import Employee from app.models.recognition_log import RecognitionLog from app.models.shift import Shift class AttendanceService: 考勤业务服务类。 staticmethod def _combine_datetime(d: date, t) - datetime: 将日期与时间组合为 datetime。 return datetime.combine(d, t) def punch( self, db: Session, employee: Employee, similarity: float, image_path: str, ) - Attendance: 处理员工刷脸打卡签到/签退。 today date.today() now datetime.now() record ( db.query(Attendance) .filter( Attendance.employee_id employee.id, Attendance.attendance_date today, ) .first() ) shift None if employee.shift_id: shift db.query(Shift).filter(Shift.id employee.shift_id).first() if not record: status self._calc_check_in_status(now, shift) record Attendance( employee_idemployee.id, attendance_datetoday, check_in_timenow, check_in_imageimage_path, statusstatus, similarityDecimal(str(round(similarity, 4))), ) db.add(record) else: if record.check_out_time: raise ValueError(今日已完成签退无需重复打卡) status self._calc_check_out_status(now, shift, record.status) record.check_out_time now record.check_out_image image_path record.status status if record.check_in_time: hours (record.check_out_time - record.check_in_time).total_seconds() / 3600 record.work_hours Decimal(str(round(hours, 2))) record.similarity Decimal(str(round(similarity, 4))) db.commit() db.refresh(record) return record def _calc_check_in_status(self, check_time: datetime, shift: Optional[Shift]) - str: 根据班次判定签到状态。 if not shift: return 正常 start_dt self._combine_datetime(check_time.date(), shift.start_time) tolerance timedelta(minutesshift.late_tolerance or 0) if check_time start_dt tolerance: return 正常 return 迟到 def _calc_check_out_status( self, check_time: datetime, shift: Optional[Shift], current_status: str ) - str: 根据班次判定签退状态。 if not shift: return current_status if current_status ! 迟到 else 迟到 end_dt self._combine_datetime(check_time.date(), shift.end_time) tolerance timedelta(minutesshift.early_tolerance or 0) is_early check_time end_dt - tolerance if current_status 迟到 and is_early: return 迟到且早退 if current_status 迟到: return 迟到 if is_early: return 早退 return 正常 def get_dashboard_stats(self, db: Session) - dict: 获取首页统计数据。 today date.today() total_employees db.query(Employee).filter(Employee.status 1).count() today_attendance ( db.query(Attendance) .filter(Attendance.attendance_date today, Attendance.check_in_time.isnot(None)) .count() ) today_late ( db.query(Attendance) .filter( Attendance.attendance_date today, Attendance.status.in_([迟到, 迟到且早退]), ) .count() ) from app.models.face import Face face_count db.query(Face).count() total_logs db.query(RecognitionLog).count() success_logs db.query(RecognitionLog).filter(RecognitionLog.success 1).count() success_rate round(success_logs / total_logs * 100, 1) if total_logs else 0 trend [] for i in range(6, -1, -1): d today - timedelta(daysi) count ( db.query(Attendance) .filter(Attendance.attendance_date d, Attendance.check_in_time.isnot(None)) .count() ) trend.append({date: d.strftime(%Y-%m-%d), count: count}) status_stats [] for status_name in [正常, 迟到, 早退, 迟到且早退, 缺卡]: cnt ( db.query(Attendance) .filter(Attendance.attendance_date today, Attendance.status status_name) .count() ) status_stats.append({name: status_name, value: cnt}) dept_stats [] departments db.query(Department).filter(Department.status 1).all() for dept in departments: cnt ( db.query(Employee) .filter(Employee.department_id dept.id, Employee.status 1) .count() ) dept_stats.append({name: dept.name, value: cnt}) return { total_employees: total_employees, today_attendance: today_attendance, today_late: today_late, face_count: face_count, success_rate: success_rate, trend: trend, status_stats: status_stats, dept_stats: dept_stats, } attendance_service AttendanceService()template div classpage-container div classpage-header h2 classpage-title岗位管理/h2 el-button typeprimary clickopenDialog()新增岗位/el-button /div div classcard-panel div classsearch-bar el-input v-modelquery.keyword placeholder搜索岗位名称/编码 clearable stylewidth: 240px clearloadData / el-button typeprimary clickloadData搜索/el-button /div el-table :datatableData stripe border el-table-column propname label岗位名称 min-width140 show-overflow-tooltip / el-table-column propcode label岗位编码 min-width120 show-overflow-tooltip / el-table-column propsort_order label排序 min-width80 / el-table-column label状态 min-width80 template #default{ row } el-tag :typerow.status 1 ? success : danger{{ row.status 1 ? 启用 : 禁用 }}/el-tag /template /el-table-column el-table-column label操作 min-width160 fixedright template #default{ row } el-button link typeprimary clickopenDialog(row)编辑/el-button el-button link typedanger clickhandleDelete(row)删除/el-button /template /el-table-column /el-table el-pagination classpagination v-model:current-pagequery.page v-model:page-sizequery.page_size :totaltotal layouttotal, sizes, prev, pager, next changeloadData / /div el-dialog v-modeldialogVisible :titleform.id ? 编辑岗位 : 新增岗位 width500px el-form :modelform label-width80px el-form-item label名称el-input v-modelform.name //el-form-item el-form-item label编码el-input v-modelform.code //el-form-item el-form-item label排序el-input-number v-modelform.sort_order :min0 //el-form-item el-form-item label状态el-switch v-modelform.status :active-value1 :inactive-value0 //el-form-item /el-form template #footer el-button clickdialogVisible false取消/el-button el-button typeprimary clickhandleSave确定/el-button /template /el-dialog /div /template script setup /** 岗位管理页面 */ import { ref, reactive, onMounted } from vue import { ElMessage, ElMessageBox } from element-plus import { positionApi } from /api const tableData ref([]); const total ref(0); const dialogVisible ref(false) const query reactive({ page: 1, page_size: 10, keyword: }) const form reactive({ id: null, name: , code: , sort_order: 0, status: 1 }) async function loadData() { const res await positionApi.list(query); tableData.value res.data.list; total.value res.data.total } function openDialog(row) { Object.assign(form, row ? { ...row } : { id: null, name: , code: , sort_order: 0, status: 1 }); dialogVisible.value true } async function handleSave() { form.id ? await positionApi.update(form.id, form) : await positionApi.create(form); ElMessage.success(保存成功); dialogVisible.value false; loadData() } async function handleDelete(row) { await ElMessageBox.confirm(确定删除, 提示, { type: warning }); await positionApi.remove(row.id); ElMessage.success(删除成功); loadData() } onMounted(loadData) /script style scoped.pagination { margin-top: 16px; justify-content: flex-end; }/style