You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL-Flask考勤系统:登录校验、会话时长计算及薪资核算方案问询

考勤管理系统(MySQL + Flask)技术实现方案

我来给你梳理下用MySQL和Flask实现这个考勤管理系统的具体方案,刚好我之前做过类似的项目,应该能帮到你:


一、核心数据库表设计

首先得把数据存储的结构搭好,这是后续功能的基础:

1. 用户表(存储员工基础信息)

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) UNIQUE NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    department VARCHAR(50),
    base_salary DECIMAL(10,2) COMMENT '员工基本工资',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2. 考勤记录表(存储每日登录/登出、状态数据)

CREATE TABLE attendance_records (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    login_time DATETIME,
    logout_time DATETIME,
    session_duration INT COMMENT '会话时长(分钟)',
    status ENUM('present', 'leave', 'absent') DEFAULT 'present',
    record_date DATE NOT NULL,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    INDEX idx_user_date (user_id, record_date) -- 优化查询性能
);

3. 月度统计表(按月聚合数据,方便薪资核算)

CREATE TABLE monthly_attendance_stats (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    year INT NOT NULL,
    month INT NOT NULL,
    total_work_minutes INT DEFAULT 0,
    leave_days INT DEFAULT 0,
    absent_days INT DEFAULT 0,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
    UNIQUE KEY idx_user_year_month (user_id, year, month) -- 避免重复统计
);

二、核心功能实现

1. 校验用户当日是否至少登录一次

写一个Flask接口或者业务函数,查询当日该用户的考勤记录即可:

from flask import request, jsonify
from datetime import datetime
import mysql.connector

# 初始化数据库连接(实际项目建议用连接池)
db = mysql.connector.connect(
    host="localhost",
    user="your_db_user",
    password="your_db_pwd",
    database="attendance_system"
)

@app.route('/api/check-daily-login', methods=['POST'])
def check_daily_login():
    data = request.get_json()
    user_id = data.get('user_id')
    today = datetime.today().date()

    cursor = db.cursor(dictionary=True)
    query = """
        SELECT COUNT(*) AS login_count 
        FROM attendance_records 
        WHERE user_id = %s AND record_date = %s
    """
    cursor.execute(query, (user_id, today))
    result = cursor.fetchone()

    return jsonify({
        "status": "success",
        "has_logged_in": result['login_count'] > 0
    })

2. 计算登录会话时长

分两步:用户登录时记录登录时间,登出时计算时长;同时处理忘记登出的情况:

# 用户登录接口
@app.route('/api/user-login', methods=['POST'])
def user_login():
    data = request.get_json()
    user_id = data.get('user_id')
    login_time = datetime.now()
    record_date = login_time.date()

    cursor = db.cursor()
    query = """
        INSERT INTO attendance_records (user_id, login_time, record_date)
        VALUES (%s, %s, %s)
    """
    cursor.execute(query, (user_id, login_time, record_date))
    db.commit()
    return jsonify({"status": "success", "login_record_id": cursor.lastrowid})

# 用户登出接口
@app.route('/api/user-logout', methods=['POST'])
def user_logout():
    data = request.get_json()
    login_record_id = data.get('login_record_id')
    logout_time = datetime.now()

    cursor = db.cursor(dictionary=True)
    # 获取对应的登录时间
    cursor.execute("SELECT login_time FROM attendance_records WHERE id = %s", (login_record_id,))
    login_record = cursor.fetchone()

    if not login_record:
        return jsonify({"status": "error", "message": "登录记录不存在"}), 404

    # 计算会话时长(分钟)
    session_duration = int((logout_time - login_record['login_time']).total_seconds() / 60)
    # 更新记录
    update_query = """
        UPDATE attendance_records 
        SET logout_time = %s, session_duration = %s 
        WHERE id = %s
    """
    cursor.execute(update_query, (logout_time, session_duration, login_record_id))
    db.commit()
    return jsonify({"status": "success", "session_duration": session_duration})

# 处理未主动登出的会话(每天凌晨执行)
def handle_unfinished_sessions():
    yesterday = datetime.today().date() - datetime.timedelta(days=1)
    # 假设默认下班时间为18:00
    default_logout_time = datetime.combine(yesterday, datetime.strptime("18:00", "%H:%M").time())

    cursor = db.cursor()
    query = """
        UPDATE attendance_records 
        SET logout_time = %s, session_duration = TIMESTAMPDIFF(MINUTE, login_time, %s)
        WHERE record_date = %s AND logout_time IS NULL
    """
    cursor.execute(query, (default_logout_time, default_logout_time, yesterday))
    db.commit()

3. 标记当日未登录用户为请假

用定时任务每天凌晨执行,批量处理未登录用户:

from flask_apscheduler import APScheduler

scheduler = APScheduler()

def mark_absent_users_as_leave():
    today = datetime.today().date()
    cursor = db.cursor(dictionary=True)

    # 获取所有用户ID
    cursor.execute("SELECT id FROM users")
    all_users = [user['id'] for user in cursor.fetchall()]

    # 获取当日已登录用户ID
    cursor.execute("SELECT DISTINCT user_id FROM attendance_records WHERE record_date = %s", (today,))
    logged_in_users = [user['user_id'] for user in cursor.fetchall()]

    # 找出未登录用户
    absent_users = set(all_users) - set(logged_in_users)

    # 插入请假记录
    insert_query = """
        INSERT INTO attendance_records (user_id, record_date, status)
        VALUES (%s, %s, 'leave')
    """
    for user_id in absent_users:
        cursor.execute(insert_query, (user_id, today))
    db.commit()

# 配置定时任务:每天凌晨1点执行
scheduler.add_job(
    id='mark_leave_job',
    func=mark_absent_users_as_leave,
    trigger='cron',
    hour=1,
    minute=0
)

scheduler.init_app(app)
scheduler.start()

4. 按月存储统计数据

每月最后一天凌晨执行,聚合当月数据到月度统计表:

def generate_monthly_stats():
    now = datetime.now()
    # 处理跨年情况(比如1月的上个月是去年12月)
    if now.month == 1:
        stat_year = now.year - 1
        stat_month = 12
    else:
        stat_year = now.year
        stat_month = now.month - 1

    cursor = db.cursor(dictionary=True)
    # 统计每个用户当月的工作时长、请假天数
    query = """
        SELECT 
            user_id,
            SUM(CASE WHEN status = 'present' THEN session_duration ELSE 0 END) AS total_work_minutes,
            SUM(CASE WHEN status = 'leave' THEN 1 ELSE 0 END) AS leave_days,
            SUM(CASE WHEN status = 'absent' THEN 1 ELSE 0 END) AS absent_days
        FROM attendance_records
        WHERE YEAR(record_date) = %s AND MONTH(record_date) = %s
        GROUP BY user_id
    """
    cursor.execute(query, (stat_year, stat_month))
    user_stats = cursor.fetchall()

    # 插入或更新月度统计(避免重复)
    upsert_query = """
        INSERT INTO monthly_attendance_stats (user_id, year, month, total_work_minutes, leave_days, absent_days)
        VALUES (%s, %s, %s, %s, %s, %s)
        ON DUPLICATE KEY UPDATE
            total_work_minutes = VALUES(total_work_minutes),
            leave_days = VALUES(leave_days),
            absent_days = VALUES(absent_days)
    """
    for stat in user_stats:
        cursor.execute(upsert_query, (
            stat['user_id'],
            stat_year,
            stat_month,
            stat['total_work_minutes'] or 0,
            stat['leave_days'] or 0,
            stat['absent_days'] or 0
        ))
    db.commit()

# 配置定时任务:每月最后一天凌晨2点执行
scheduler.add_job(
    id='monthly_stats_job',
    func=generate_monthly_stats,
    trigger='cron',
    day='last',
    hour=2,
    minute=0
)

三、薪资核算扩展

有了月度统计表,薪资计算就非常便捷了,示例逻辑如下:

def calculate_monthly_salary(user_id, year, month):
    cursor = db.cursor(dictionary=True)
    # 获取用户基本工资
    cursor.execute("SELECT base_salary FROM users WHERE id = %s", (user_id,))
    user = cursor.fetchone()
    if not user:
        return None

    base_salary = user['base_salary']
    # 获取月度考勤统计
    cursor.execute("""
        SELECT total_work_minutes, leave_days 
        FROM monthly_attendance_stats 
        WHERE user_id = %s AND year = %s AND month = %s
    """, (user_id, year, month))
    stats = cursor.fetchone()

    if not stats:
        return 0  # 无记录时按规则处理,这里默认0

    # 假设每月标准工作时长为22天×8小时=10560分钟
    standard_minutes = 22 * 8 * 60
    # 按实际工作时长比例计算基本工资
    work_salary = base_salary * (stats['total_work_minutes'] / standard_minutes)
    # 请假扣薪:每天扣薪为基本工资/22
    leave_deduction = (base_salary / 22) * stats['leave_days']
    total_salary = work_salary - leave_deduction

    return round(total_salary, 2)

内容的提问来源于stack exchange,提问作者Bhanu vikas Yaganti

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 07:05:34