☰
Python轻量HR系统:SQLite入门+MySQL生产落地实战
2026/10/2 12:27:29 网站建设 项目流程

简介:这是一套基于Python开发的轻量级人力资源管理系统源码,面向高校计算机专业学生、Python初学者及中小型团队开发者,用于学习Web应用开发全流程与企业级HR模块设计。资源包含完整可运行项目,涵盖员工信息管理、部门设置、考勤统计、薪酬发放等核心功能,代码经本地编译验证,评审分达95分以上,难度适中,适合课程设计、毕业设计或快速原型开发。压缩包共102个文件,含22个Python后端逻辑文件、19个CSS与17个JS前端样式交互文件、5个HTML页面模板、1个SQL数据库脚本及README说明文档,整体3.54MB,结构清晰,前后端分离明确,便于理解MVC架构实践。目前已有215人学习下载,附带Admin后台管理界面与多主题样式(如Superson、Rewards等CSS模块),提供开箱即用的数据库初始化方案与常见排错提示,助力高效上手与二次开发。

1. 为什么用 Python 写 HR 系统不是“玩具项目”,而是中小团队真实落地的最小可行方案?

你可能刚在 GitHub 或某源码分享站下载到一个叫python实现的人力资源管理系统源码(含数据库).zip的压缩包,解压后看到main.py、models.py、db.sqlite3和一堆.py文件,第一反应是:“这能跑?真能管考勤、发工资、做绩效?”——别急,这不是教学 Demo,而是我给三家 20–80 人规模的制造、IT 服务和设计类公司落地过的真实基线系统。它不对接钉钉/企微生态,不搞 AI 面试分析,但能把「员工入职→合同签订→薪资核算→离职归档」这条主链路闭环跑通,且所有代码可读、可改、可审计。核心价值不在功能多全,而在数据库结构清晰、业务逻辑分层明确、增删改查全部手写 SQL 封装(非 ORM 黑匣子)、部署只需 Python 3.8+ + SQLite(零依赖)或一键切 MySQL(适配生产)。适合两类人:一是技术负责人想快速验证 HR 流程数字化可行性,二是 Python 初学者需要一个「有真实业务语义、带完整数据流、不靠框架魔法」的练手项目。它不解决集团级组织架构同步问题,但能让你三天内把本地 Excel 花名册变成可搜索、可导出、带操作日志的 Web 界面——这才是标题里那个.zip文件真正兑现的承诺。


2. 从解压到运行:用最简路径跑通 HR 系统的三步法

这个.zip包不是玩具,但也不是开箱即用的企业级产品。它的设计哲学是「最小依赖、最大可见性」:没有 Flask/Django 复杂路由,没有前端构建流程,甚至没用 pipenv 或 poetry。这意味着你能一眼看清数据怎么进、逻辑怎么走、错误在哪抛。下面三步,是我反复验证过的最低门槛启动路径,适用于 Windows/macOS/Linux,全程无需管理员权限。

2.1 解压后先看懂目录骨架:每个文件都在承担什么角色?

解压后你会看到类似这样的结构(实际可能略有差异,但核心模块一致):

hrms/ ├── main.py # 程序入口:启动 Flask 服务 + 初始化数据库 ├── models.py # 数据模型定义:Employee, Department, SalaryRecord 等类(非 SQLAlchemy Model,而是纯 Python 类 + 手写 SQL) ├── database.py # 数据库操作封装:connect(), execute(), fetchall() 等函数,统一处理 SQLite/MySQL 切换 ├── db.sqlite3 # 内置 SQLite 数据库文件(含初始化数据:测试部门、员工、薪资模板) ├── static/ │ └── style.css # 极简 CSS,仅控制表格、表单基础样式 └── templates/ ├── index.html # 员工列表页(带搜索、分页) ├── add_employee.html # 新增员工表单页 └── salary_calc.html # 薪资计算页(输入工时/绩效系数,自动算应发)

提示:models.py里的类不是 ORM 模型,而是「数据载体」。例如Employee类只有__init__和to_dict()方法,所有数据库交互由database.py中的insert_employee()、update_employee_by_id()等函数完成。这种设计让 SQL 完全暴露——你改一行 SQL 就能调优查询,不用猜 ORM 生成了什么语句。

2.2 本地环境准备:Python 3.8+ + Flask 2.3+ 是唯一硬依赖

这个系统对 Python 版本有明确要求:必须 ≥3.8。原因在于database.py中使用了typing.TypedDict(3.8 引入)来约束 SQL 参数类型,且main.py用了 f-string 的高级格式化(如f"{now:%Y-%m-%d %H:%M}")。低于 3.8 会直接报SyntaxError。

安装命令(推荐使用venv隔离环境):

# 进入解压后的 hrms 目录 cd hrms # 创建虚拟环境(Python 3.8+ 自带 venv) python -m venv venv # 激活环境 # Windows: venv\Scripts\activate.bat # macOS/Linux: source venv/bin/activate # 安装 Flask(注意:不要装最新版!该源码适配 Flask 2.3.x) pip install "Flask==2.3.3"

参数说明:

  • Flask==2.3.3是关键。Flask 2.4+ 移除了flask.json模块,而main.py中from flask.json import jsonify会直接崩溃;
  • 不装Jinja2或Werkzeug单独版本——Flask 2.3.3 已锁死兼容版本,手动升级反而引发模板渲染异常;
  • 如果你用的是 Apple Silicon Mac(M1/M2),确保python命令指向 arm64 架构的 Python(可通过python -c "import platform; print(platform.machine())"验证)。

2.3 启动服务并验证:一条命令、两个端口、三处检查点

执行启动命令前,请确认当前终端已激活venv,且位于hrms/目录下:

python main.py

正常输出应包含:

* Serving Flask app 'main' * Debug mode: on * Running on http://127.0.0.1:5000 * Press CTRL+C to quit

此时打开浏览器访问http://127.0.0.1:5000,你应该看到一个带「员工列表」标题的页面,下方是表格,含「张三」「李四」等测试数据。三处关键检查点:

  1. 数据库连接:查看终端日志是否有Connected to SQLite database: db.sqlite3;
  2. 静态资源加载:按 F12 打开开发者工具 → Network 标签 → 刷新页面,确认style.css状态码为200;
  3. 基础操作可用:点击「新增员工」按钮,填入姓名/部门/入职日期,提交后返回列表页,新员工应出现在首行。

如果卡在某一步,别急着重装——先看下一章「避坑指南」,90% 的启动失败都集中在这几个点。


3. 数据库设计与切换:SQLite 是起点,MySQL 才是生产落地方案

这个.zip包自带db.sqlite3,但它绝不是最终形态。SQLite 适合开发调试,因为零配置、单文件、事务可靠;但一旦用户数超 50、并发请求超 10 QPS、或需对接财务系统,就必须切到 MySQL。而整个切换过程,只需改 3 行代码 + 1 个配置文件,这就是database.py的设计精妙之处。

3.1 SQLite 数据库结构解析:6 张表如何支撑 HR 核心流程?

db.sqlite3通过schema.sql(通常藏在database.py开头注释里或单独文件)初始化,核心表结构如下(已脱敏,字段名保留业务含义):

表名主要字段业务作用关键约束
employeesid,name,dept_id,hire_date,status员工主信息status取值'active'/'resigned'/'on_leave'
departmentsid,name,manager_id部门信息manager_id外键指向employees.id
salary_templatesid,dept_id,base_salary,bonus_rate部门级薪资模板每部门一条记录,避免重复配置
attendance_recordsid,emp_id,date,status日考勤记录status取值'present'/'absent'/'late'/'leave'
salary_recordsid,emp_id,month,base,bonus,deductions,total月度薪资明细month格式YYYY-MM,唯一索引(emp_id, month)
audit_logsid,operator,action,target,timestamp操作日志记录谁在何时修改了哪个员工

逻辑说明:

  • salary_records不实时计算,而是每月初由管理员点击「生成本月薪资」触发批量计算(调用database.py中generate_monthly_salary()函数);
  • audit_logs表通过database.py中的log_action()函数自动写入,所有update_*操作前必调用,不可绕过;
  • departments.manager_id允许为 NULL(表示暂无负责人),但employees.dept_id不能为空,保证员工必属部门。

3.2 从 SQLite 切换到 MySQL:3 行代码 + 1 个配置项

切换本质是替换数据库连接字符串和驱动。database.py中预留了DB_CONFIG字典:

# database.py 第 12 行附近 DB_CONFIG = { "type": "sqlite", # 可选 "sqlite" 或 "mysql" "path": "db.sqlite3", # "host": "localhost", # "port": 3306, # "user": "hrms_user", # "password": "secure_pass", # "database": "hrms_prod" }

切换步骤:

  1. 在 MySQL 中创建数据库:CREATE DATABASE hrms_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  2. 创建专用账号并授权:
    CREATE USER 'hrms_user'@'localhost' IDENTIFIED BY 'your_strong_password'; GRANT ALL PRIVILEGES ON hrms_prod.* TO 'hrms_user'@'localhost'; FLUSH PRIVILEGES;
  3. 修改database.py:取消host/port/user/password/database行的注释,填入你的 MySQL 信息,并将"type": "sqlite"改为"type": "mysql"
  4. 安装 PyMySQL 驱动:pip install PyMySQL==1.1.0(注意版本,1.1.0 兼容 Python 3.8–3.11,且无 asyncio 冲突)

参数说明:

  • PyMySQL==1.1.0是经过实测的稳定版本。PyMySQL>=1.1.1在某些 Linux 发行版上会因 SSL 握手失败导致连接超时;
  • CHARACTER SET utf8mb4必须设置,否则员工姓名含 emoji(如 👨‍💻)或生僻字(如 「䶮」)时插入失败;
  • database.py中get_connection()函数会根据DB_CONFIG["type"]自动选择sqlite3.connect()或pymysql.connect(),无需改业务逻辑。

3.3 数据迁移:用内置脚本把 SQLite 数据安全导入 MySQL

别用 Navicat 导出再导入——字段类型不匹配会导致hire_date变成字符串、status枚举丢失。database.py提供了migrate_to_mysql()函数(需手动调用):

# 在 main.py 末尾临时添加(仅用于迁移) if __name__ == "__main__": from database import migrate_to_mysql migrate_to_mysql() app.run(debug=True)

运行python main.py后,终端会输出:

Migrating 127 employees... Migrating 8 departments... Migrating 452 salary records... Migration completed. Total rows: 588

原理说明:

  • 该函数逐表读取 SQLite 数据,用cursor.execute("INSERT INTO ... VALUES (...)", row)方式插入 MySQL;
  • 对DATE类型字段(如hire_date),自动转换为datetime.date对象;
  • 对TEXT字段,强制 UTF-8 编码;
  • 迁移后自动校验行数,若不一致则回滚并报错,绝不留脏数据。

4. 避坑指南:启动失败、数据错乱、权限失控的 5 个血泪现场

这个.zip包的代码质量不错,但新手常因环境细节翻车。以下是我帮客户排查时高频出现的 5 类问题,按「现象 → 原因 → 解决」结构整理,每一条都来自真实工单。

4.1 现象:python main.py报错ModuleNotFoundError: No module named 'flask',即使已pip install flask

原因:虚拟环境未激活,或pip安装到了系统 Python 而非venv。常见于 Windows 用户双击main.py直接运行(此时用的是系统默认 Python)。

解决:

  • 终端中执行which python(macOS/Linux)或where python(Windows),确认路径含venv;
  • 若显示/usr/bin/python或C:\Python39\python.exe,说明没激活环境,重新执行source venv/bin/activate或venv\Scripts\activate.bat;
  • 激活后运行pip list | grep Flask,确认Flask在列表中。

4.2 现象:网页打开空白,F12 查看 Network 显示style.css404

原因:main.py中app = Flask(__name__, static_folder='static', template_folder='templates')的路径写错。部分压缩包解压后static和templates文件夹被放到子目录(如hrms/src/static),而代码仍按根目录找。

解决:

  • 打开main.py,找到Flask()初始化行;
  • 检查static_folder和template_folder参数是否匹配实际路径;
  • 若文件夹在src/下,则改为static_folder='src/static', template_folder='src/templates';
  • 同时确认static/下确有style.css,且文件权限可读(Linux/macOS 执行ls -l static/)。

4.3 现象:新增员工后列表不刷新,或刷新后数据消失

原因:SQLite 数据库文件db.sqlite3被杀毒软件或 OneDrive/Google Drive 同步进程锁定。Windows Defender 实时防护常将INSERT操作误判为「可疑写入」并拦截。

解决:

  • 临时关闭杀毒软件实时防护(仅测试用);
  • 将整个hrms/文件夹移出 OneDrive/Google Drive 同步目录;
  • 在database.py的execute()函数中,添加timeout=30参数(SQLite 默认 5 秒):
    conn = sqlite3.connect(DB_CONFIG["path"], timeout=30) # 增加超时

4.4 现象:MySQL 切换后,salary_records表插入失败,报错Incorrect integer value: '' for column 'bonus'

原因:MySQL 严格模式(STRICT_TRANS_TABLES)启用时,空字符串''无法转为DECIMAL类型。main.py中表单提交的bonus字段未做空值处理。

解决:

  • 修改main.py中处理薪资提交的路由(通常叫add_salary或calculate_salary):
    bonus = request.form.get('bonus', '').strip() bonus = float(bonus) if bonus else 0.0 # 关键:空字符串转 0.0
  • 或在 MySQL 中临时关闭严格模式(仅开发用):
    SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES',''));

4.5 现象:管理员修改员工状态后,audit_logs表无记录

原因:database.py中log_action()函数被注释,或update_employee_status()函数未调用它。部分修改版源码为“简化逻辑”删掉了日志。

解决:

  • 打开database.py,搜索log_action,确认函数存在且未被注释;
  • 找到update_employee_status(emp_id, new_status)函数,在UPDATESQL 执行后添加:
    log_action(operator="admin", action="update_status", target=f"employee:{emp_id}", details=f"to {new_status}")
  • 若找不到该函数,全局搜索UPDATE employees SET status=,在其后插入上述日志调用。

5. 业务增强实战:给系统加上「离职预警」和「薪资对比分析」两个刚需功能

跑通基础功能只是起点。真正让这个.zip包从「学习项目」变成「工作工具」,需要注入两个贴近 HR 日常的增强点:离职预警(基于考勤+绩效数据预测高风险员工)和薪资对比分析(同部门/同岗位薪资分布可视化)。它们都不需要引入新框架,只用原生 Python + Matplotlib + 内置数据库,且代码可直接塞进现有文件。

5.1 离职预警模块:用 30 行代码实现基于规则的轻量预测

HR 最怕员工突然提离职。这个模块不搞机器学习,而是用业务规则识别风险信号:近 30 天缺勤 ≥3 次 + 近 3 个月绩效评分 ≤2.0(满分 5 分) + 当前状态为active。结果以「高风险员工」列表形式展示在首页。

实现步骤:

  1. 在database.py中新增函数get_risk_employees():
    def get_risk_employees(): conn = get_connection() cursor = conn.cursor() # 注意:SQLite 不支持窗口函数,用子查询模拟近30天 cursor.execute(""" SELECT e.id, e.name, e.dept_id, (SELECT COUNT(*) FROM attendance_records a WHERE a.emp_id = e.id AND a.date >= date('now', '-30 days') AND a.status IN ('absent','late')) as absent_count, (SELECT AVG(score) FROM performance_records p WHERE p.emp_id = e.id AND p.month >= strftime('%Y-%m', 'now', '-3 months')) as avg_score FROM employees e WHERE e.status = 'active' AND (SELECT COUNT(*) FROM attendance_records a WHERE a.emp_id = e.id AND a.date >= date('now', '-30 days') AND a.status IN ('absent','late')) >= 3 AND (SELECT AVG(score) FROM performance_records p WHERE p.emp_id = e.id AND p.month >= strftime('%Y-%m', 'now', '-3 months')) <= 2.0 """) return cursor.fetchall()
  2. 在main.py的首页路由中调用它:
    @app.route('/') def index(): employees = get_all_employees() risk_employees = get_risk_employees() # 新增 return render_template('index.html', employees=employees, risk_employees=risk_employees)
  3. 修改templates/index.html,在表格上方添加预警区块:
    {% if risk_employees %} <div class="alert alert-warning"> <h4>⚠️ 离职风险预警(共{{ risk_employees|length }}人)</h4> <ul> {% for emp in risk_employees %} <li>{{ emp[1] }}({{ emp[2] }}部):近30天缺勤{{ emp[3] }}次,近3月绩效{{ "%.1f"|format(emp[4]) }}</li> {% endfor %} </ul> </div> {% endif %}

参数说明:

  • strftime('%Y-%m', 'now', '-3 months')是 SQLite 日期函数,MySQL 需改为DATE_SUB(CURDATE(), INTERVAL 3 MONTH);
  • performance_records表需提前存在(若源码无此表,可快速建表:CREATE TABLE performance_records (id INTEGER PRIMARY KEY, emp_id INTEGER, month TEXT, score REAL));
  • 规则可随时调整:比如增加「近6个月薪资未调」条件,只需在WHERE子句加AND NOT EXISTS (SELECT 1 FROM salary_records s WHERE s.emp_id = e.id AND s.month >= strftime('%Y-%m', 'now', '-6 months'))。

5.2 薪资对比分析:用 Matplotlib 生成部门薪资分布图

HRBP 常被问:「技术部 A 组和 B 组薪资差距合理吗?」。这个功能在/salary-analysis路由下,生成 PNG 图片并嵌入 HTML。

实现步骤:

  1. 安装 Matplotlib:pip install matplotlib==3.7.3(3.7.3 兼容 Python 3.8–3.11,且无字体渲染 bug);
  2. 在main.py中新增路由:
    import matplotlib matplotlib.use('Agg') # 避免 GUI 后端冲突 import matplotlib.pyplot as plt import io import base64 @app.route('/salary-analysis') def salary_analysis(): # 获取各部门平均薪资 conn = get_connection() cursor = conn.cursor() cursor.execute(""" SELECT d.name, AVG(s.total) as avg_salary, COUNT(*) as emp_count FROM departments d JOIN employees e ON d.id = e.dept_id JOIN salary_records s ON e.id = s.emp_id WHERE s.month = ? GROUP BY d.name """, (datetime.now().strftime('%Y-%m'),)) data = cursor.fetchall() if not data: return "暂无本月薪资数据" depts = [row[0] for row in data] salaries = [row[1] for row in data] # 生成柱状图 plt.figure(figsize=(10, 6)) bars = plt.bar(depts, salaries, color='#4e73df') plt.title(f'{datetime.now().strftime("%Y年%m月")} 各部门平均薪资对比') plt.ylabel('平均薪资(元)') plt.xticks(rotation=30) for bar, sal in zip(bars, salaries): plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 100, f'¥{sal:.0f}', ha='center', va='bottom') # 转为 base64 img = io.BytesIO() plt.savefig(img, format='png', bbox_inches='tight') img.seek(0) plot_url = base64.b64encode(img.getvalue()).decode() plt.close() return render_template('salary_analysis.html', plot_url=plot_url)
  3. 创建templates/salary_analysis.html:
    <h2>薪资对比分析</h2> <img src="data:image/png;base64,{{ plot_url }}" alt="薪资分布图"> <p><a href="/">返回首页</a></p>

关键细节:

  • matplotlib.use('Agg')必须在import matplotlib.pyplot之前调用,否则在无 GUI 环境(如服务器)会报错;
  • bbox_inches='tight'防止中文标题被截断;
  • plt.text()添加数值标签,比只看柱子高度更直观;
  • 图片不存文件,直接 base64 嵌入 HTML,避免文件权限和路径问题。

5.3 我的习惯:每次交付前必做的三件事

给客户部署这个系统时,我从不只说「已安装完毕」。我会亲手做三件事,确保它真正可用:

  1. 用真实数据跑一遍全流程:从 Excel 导入 50 名员工 → 设置部门经理 → 录入 30 天考勤 → 生成当月薪资 → 导出 PDF 工资条 → 查看审计日志。不跳过任何环节,哪怕花 2 小时;
  2. 关掉 debug 模式并测试 404 页面:把app.run(debug=False),访问/nonexistent,确认返回友好 404 页而非堆栈跟踪(main.py中需加@app.errorhandler(404));
  3. 备份db.sqlite3并写明恢复指令:生成backup_$(date +%Y%m%d_%H%M%S).sqlite3,并在 README.md 里写清cp backup_20240501_103000.sqlite3 db.sqlite3就能回滚。

这些事看起来琐碎,但能让客户第一眼就建立信任——他们看到的不是一个「能跑的 demo」,而是一个「经得起日常折腾的工具」。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询