ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Flask与微信小程序的建筑工地员工考勤请假系统设计与实现

基于Flask与微信小程序的建筑工地员工考勤请假系统设计与实现 做建筑工程的都知道工地现场人员管理一直是老大难问题。工人分散在各个项目现场靠微信语音汇报、纸质表格签字、月底反复对账考勤和请假数据经常对不上。我前两年用Python Flask和微信小程序搭了一个建筑公司内部的员工请假考勤签到系统今天把完整的设计思路、后端接口、小程序联调以及踩过的坑全部整理出来。这套方案不依赖第三方云服务自己有一台服务器就能跑适合中小型建筑企业、装修公司、劳务分包队做员工管理前端用微信小程序实现员工不用装额外App打开微信就能用管理者在后台直接审批请假、查看签到记录实测下来比纸质流程至少节省一半沟通时间。1. 项目概述与核心需求拆解1.1 这个系统到底解决什么问题建筑公司的考勤场景和普通办公室完全不一样。办公室员工固定工位、固定时间打个卡就行工地现场是多个项目并行木工、瓦工、电工往往同时跨几个工地干活项目经理要同时管理几十号人的出勤情况。传统的做法是每个工地放一本签到本工人到了自己手写签名月底由班组长统一汇总交给公司。问题很快暴露字迹潦草看不清、漏签代签严重、请假信息不同步。有个真实案例是我朋友所在的劳务公司月底核算工资时发现某工人考勤表上全勤但实际上他请了三天假项目经理和公司记录的完全对不上。这个系统就是为了解决这类信息断层——员工自己用手机发起请假申请出勤打卡以手机端GPS定位和签到记录为准数据实时同步到后台管理层可以随时查看每一个环节都有操作日志。1.2 为什么选Flask加微信小程序不选其他方案技术选型上我几乎没有纠结。后端框架考虑的其实有三个方向Django、Flask和Spring Boot。Django功能全但太重自带Admin后台和ORM对于这种轻量级内部工具反而显得臃肿Spring Boot适合团队协作的大型项目但需要Java环境部署成本和维护成本都偏高。最后选了Flask理由很直接Flask足够轻量一个主文件就能跑起来配合SQLAlchemy做ORM扩展性好开发效率高。我整套系统从设计到上线用了不到两周其中大部分时间花在小程序的UI调整上后端接口写了一天半就全部完成了。小程序端选微信小程序而不是H5是因为微信小程序有原生的登录能力可以直接用微信授权获取用户信息免去了账号密码注册的流程。建筑工人普遍年龄偏大你让他们记一个复杂的登录密码根本不现实让他们打开微信点一下授权登录门槛就低多了。2. 系统架构与关键设计思路2.1 整体架构小程序端加后端接口加数据库系统整体分三层前端是微信小程序中间是Flask提供的RESTful API底层是MySQL数据库也可以用SQLite快速起步但生产环境建议MySQL。微信小程序通过wx.request发送HTTPS请求到Flask接口Flask处理业务逻辑后返回JSON数据。认证方案用JWTJSON Web Token用户在微信端调用wx.login拿到code后端用code去微信服务器换取openid再通过自定义登录接口获取JWT token。之后的每一次请求都在请求头中携带token后端通过装饰器校验身份。我特意没做复杂的权限管理只设计了两种角色普通员工和管理员管理员可以审批请假、查看全部考勤记录普通员工只操作自己的页面。建筑公司通常没有细到部门层级项目经理就是管理员这种简化的权限模型足够用了。2.2 核心功能模块设计拆解下来核心模块有三个请假申请模块、考勤打卡模块、签到管理模块。请假模块需要表单页员工选择请假类型事假、病假、年假、调休填写起止日期和理由提交后进入后台审批队列管理员通过或驳回状态变化通过微信模板消息通知到员工虽然现在微信改版后订阅消息需要用户主动授权但实现逻辑是一样的。考勤打卡模块解决到底来没来的问题核心是GPS定位加时间戳小程序通过wx.getLocation获取经纬度后端比对工地坐标范围判断是否在允许的打卡范围内如果在范围内记录为正常否则记录为外勤打卡由管理员确认。签到管理模块则是更细粒度的出勤记录比如上午到场签到、下午离场签退管理员可以按项目、按日期查看所有人员的签到情况。三个模块共用一套用户体系员工的基本信息包括工号、姓名、手机号、所在项目这些信息在上传第一张考勤照片或首次登录时完善。3. 后端Flask核心实现详解3.1 环境准备与项目初始化本地开发环境我用的是Python 3.8虚拟环境用venv创建。先安装基础依赖requirements.txt文件内容如下flask2.2.3 flask-sqlalchemy3.0.3 flask-cors4.0.0 pymysql1.1.0 python-dotenv1.0.0 pyjwt2.6.0 requests2.28.2初始化Flask应用时需要注意几点。应用实例创建时一定要把config配置分开写不要让密钥、数据库地址硬编码在主文件中。我习惯用.env文件存储配置用python-dotenv加载。另外要提前配置跨域Flask端如果不在同一个域名下必然面临跨域问题。Flask-CORS库一行代码解决CORS(app)3.2 数据库模型设计数据库设计直接决定业务逻辑的复杂度。三个核心表是用户表、请假表、考勤签到表。用户表主要字段包括id、openid微信唯一标识、employee_name姓名、employee_no工号、phone手机号、project_id所属项目、role普通员工或管理员、created_at。考勤签到表是数据量最大的表每天的打卡记录都会写入一条字段包含id、user_id、attendance_type签到还是签退、latitude、longitude、location_text地址描述、status正常、外勤、迟到、created_at。这里我把签到和签退都放在同一张表里通过type字段区分业务查询时按天分组处理比单独建两张表灵活。请假表相对简单字段有id、user_id、leave_type、start_date、end_date、reason、status待审批、已通过、已驳回、approver_id、created_at。3.3 核心API接口开发接口设计遵循RESTful风格路径清晰。有几个核心接口POST /api/auth/login负责登录接收小程序端传过来的code后端用这个code去微信接口获取openid通过openid查找或创建用户返回token。这个接口必须正确处理首次登录的情况如果用户不在数据库中就直接创建一条记录给一个默认角色后续再完善信息。GET /api/attendance/status查询当前用户当天的考勤状态返回今天有没有签到、有没有签退、当前时间等信息前端小程序加载时先请求这个接口用来判断应该显示签到按钮还是签退按钮。POST /api/attendance/checkin接收经纬度和位置描述后端判断时间——假如公司规定9点上班9点30分前签到算正常超过算迟到迟到状态存入数据库。为了防作弊我记录用户提交的定位与项目地址的距离如果距离大于指定的200米直接标记为异常打卡。POST /api/leave/apply接收请假类型、起止时间、请假理由状态初始为待审批。PUT /api/leave/{id}/approve是管理员审批接口管理员请求时带上审批结果后端更新状态同时记录审批人ID。每个接口都做了统一格式返回业务前端只用判断code字段是否正确。返回格式统一为{ code: 0, message: success, data: {} }这样前后端联调时不用每个接口单独解析省了很多事。4. 微信小程序前端实现要点4.1 页面结构与导航设计小程序端我规划了四个Tab页面首页考勤打卡、请假申请、考勤记录、我的个人中心。这四个Tab基本覆盖了员工的全部操作场景没有多余的入口。页面结构上要注意微信小程序的顶部导航栏高度问题尤其在做自定义导航栏的时候不建议用全局自定义导航因为有些安卓机顶部状态栏高度不一样处理不当会导致内容被刘海屏遮挡。我用的方案是使用微信自带的navigationStyle把navigationBarTitleText设置成项目名称不做复杂改造。首页打卡页面最核心顶部展示当前时间和日期中间是一个大大的圆形签到按钮下方显示最新的打卡记录。考勤记录页面使用日历组件展示一个月的出勤情况日历中被标记为绿色的日期是正常出勤橙色是迟到红色是未打卡一目了然。4.2 核心功能页面开发首页打卡是访问最频繁的页面。这类页面的关键点在于一进入页面就不能让用户看到过时的状态所以onShow生命周期内要重新请求考勤状态接口。签到按钮的交互要明确签到成功后按钮立即变成灰色不可点击并提示今日已签到避免重复提交。考勤记录页面我使用日历插件我用的是vant-weapp的日历组件它支持标记指定日期配置很简单。请假申请页是一个表单页注意日期选择器的时间范围限制请假日期不能早于今天结束日期不能早于开始日期这些基础校验用小程序自带的picker组件实现。还有一个小细节是输入理由时如果字符太少要拦截最少5个字防止有人提交空白请假。4.3 请求封装与数据联调小程序和Flask后端联调时必须封装统一的request方法。我在utils/request.js里封装了一个请求函数自动带上token自动处理401跳转登录自动弹出错误信息。同时要在微信开发者工具中开启不校验合法域名选项因为本地开发时Flask跑在局域网ip上域名是http协议不校验才能联调。上线时必须配置HTTPS的合法域名。还有一个容易忽略的是wx.request的timeout工地现场信号可能不太好默认超时时间可以调整到10000毫秒但不能太长否则用户会以为卡死了。数据层面引入加载更多的分页功能考勤记录和请假列表都采用分页加载上拉触底时加载下一页数据避免一次渲染几十条数据导致页面卡顿。这一点在低端安卓机上格外重要亲测iPhone没问题但几百块钱的红米手机渲染大量列表时会有明显的掉帧。5. 实操过程从零搭建完整系统5.1 第一步搭建Flask项目骨架项目结构我按功能模块拆分目录清晰好维护。我的目录结构大致是attendance_backend/ ├── app.py # 主入口 ├── config.py # 配置 ├── models/ │ ├── __init__.py │ ├── user.py │ ├── leave.py │ └── attendance.py ├── api/ │ ├── __init__.py │ ├── auth.py │ ├── attendance.py │ └── leave.py ├── utils/ │ ├── jwt_utils.py │ └── resp_utils.py └── requirements.txt主入口app.py里要做几件关键事加载配置、初始化数据库、注册蓝图、启动CORS。代码大概是这样from flask import Flask from flask_cors import CORS from models import init_db from api.auth import auth_bp from api.attendance import attendance_bp from api.leave import leave_bp app Flask(__name__) app.config.from_pyfile(config.py) CORS(app) init_db(app) app.register_blueprint(auth_bp, url_prefix/api/auth) app.register_blueprint(attendance_bp, url_prefix/api/attendance) app.register_blueprint(leave_bp, url_prefix/api/leave) if __name__ __main__: app.run(host0.0.0.0, port5000)5.2 第二步配置数据库与ORM生产环境我用MySQL本地测试可以直接用SQLite切换只需改一行连接字符串。SQLAlchemy在Flask中使用时注意要在init_db(app)里执行db.create_all()但更好的做法是提前把表结构生成好避免每次启动应用都检查表存在。表结构变更时可以用Flask-Migrate管理迁移如果项目工程量不大直接用原生SQL建表也完全够用。我在models/user.py中写了User表的定义openid字段设为可空因为后续可能会支持账号密码登录但核心登录方式仍是openid。数据库连接池的问题容易被忽略。SQLAlchemy默认会维护连接池但在服务器环境上如果连接闲置过久MySQL会主动断开可能导致报错MySQL server has gone away。解决办法是在engine配置中设置pool_recycle3600让连接每隔一小时回收一次这个问题我在部署后第三天才遇到线上跑了一会儿就报错排查一圈才发现是这个原因。5.3 第三步开发核心API登录接口是第一个要写的接口逻辑也最关键。用Flask蓝图来组织接口登录代码如下auth_bp.route(/login, methods[POST]) def login(): data request.get_json() code data.get(code) # 用code换openid openid wx_code_to_openid(code) user User.query.filter_by(openidopenid).first() if not user: user User(openidopenid, roleemployee) db.session.add(user) db.session.commit() token generate_jwt(user.id, user.role) return success({token: token, user: user.to_dict()})注意wx_code_to_openid这个函数里要用微信官方接口https://api.weixin.qq.com/sns/jscode2session 传入appid和secret换openid这个请求必须放在后端执行不能在客户端调因为secret必须保密。打卡接口里需要做经纬度判断这里我简化了逻辑先把项目和经纬度配置存在配置文件中计算用户坐标和项目坐标间的距离大于指定阈值就标记异常attendance_bp.route(/checkin, methods[POST]) def checkin(): data request.get_json() lat float(data[lat]) lng float(data[lng]) dist haversine(lat, lng, PROJECT_LAT, PROJECT_LNG) if dist 200: status abnormal else: status normal # 写入数据库计算两个经纬度之间的距离用的是haversine公式不复杂但精度足够判断一个工地范围。5.4 第四步创建微信小程序页面我用的是原生微信小程序开发不引入uni-app因为这些页面交互不复杂原生最直接。app.json里注册页面路径和tabBartabBar的颜色按公司logo配色设置。首页页面top部分显示日期时间使用new Date()动态获取每秒刷新一次时间显示给员工直观的打卡反馈。中间打卡按钮采用大圆形设计直径大概150rpx点击时有个缩放动画效果增加触感反馈。这些动效用CSS transition就能实现不用额外引入动画库。请假页面用radio-group组件做请假类型的选择picker做日期选择textarea做理由输入。这些原生组件兼容性很好在微信开发者工具和真机上表现一致。特别提醒一个细节textarea在层级上有时会被原生组件盖住微信官方提供了cover-view处理但大部分场景下设置textarea的fixed属性为true就能解决问题我在开发时就遇到输入法遮挡问题调整后发现是placeholder位置的问题。5.5 第五步前后端联调与测试联调阶段最重要的是抓请求和看response。微信开发者工具的网络面板可以直接查看请求详情如果接口报错先看后端日志再看前端控制的报错内容。我习惯在每个接口的前端调用里加上console.log打印参数把出问题的参数原样输出方便对照。测试时要特别注意边界情况请假时间跨周、打卡时间刚好卡在9点30分、网络断开后重新连接。我在测试中发现最严重的一个问题是重复提交用户连续点击签到按钮会同时发出两个请求导致数据库出现两条签到记录。解决办法是前端按钮加loading状态提交后立即禁用按钮同时后端也做了一层判断查询当天是否已有签到记录有就不允许再次插入双重保险。6. 常见问题与排查技巧实录6.1 接口跨域问题本地开发时Flask运行在localhost或局域网IP小程序开发工具请求这些地址默认会跨域我配置了Flask-CORS以后问题不大。但要注意微信小程序的开发工具本地请求不需要关注CORS因为小程序不是浏览器没有同源策略的限制只要是合法域名开发模式可以跳过校验就能访问。真正的坑在于上线后必须把所有API域名配置成HTTPS且备案过的域名否则扫码真机测试时会卡在域名校验失败。我踩过这个坑第一次部署上线时用了自己服务器上未备案的域名真机一直提示request:fail url not in domain list重新备案后等了5天才通过。所以建议提前处理好备案和SSL证书。6.2 微信小程序包体积限制微信小程序主包大小限制是2MB如果代码或图片超了会出现编译失败。这个项目本身不大代码文件几十KB完全没问题。但要注意别把头像和图片资源塞到包里比如员工头像如果直接存base64字符串写入前端代码很快就会超限。我的处理方式是把头像上传到服务器数据库存图片路径前端用image组件加载网络图片。另外如果用了第三方组件库比如vant-weapp它会占几百KB的体积没关系还在2MB以内。但要提醒的是vant组件是按需引入的不要usingComponents里引入全部组件否则体积会膨胀。6.3 并发打卡与数据一致性工地早晨上班时间段非常集中7点50到8点05分之间可能同时有几十个人点击打卡。Flask默认的server是单线程的并发请求会导致排队变慢甚至超时。解决方法是部署时使用gunicorn作为生产服务器并配置多个worker进程例如gunicorn -w 4 -b 0.0.0.0:5000 app:app。数据库层面我加了唯一约束UNIQUE(user_id, attendance_type, date)保证一个人同一天不会插入重复的签到记录但如果加了唯一约束业务代码中就要处理IntegrityError异常否则并发冲突时会直接报500。实践中我在打卡接口里做了try/except捕获唯一约束冲突后返回今日已签到的提示而不是报内部错误。6.4 定位权限与精度问题打卡功能依赖wx.getLocation但微信小程序使用这个接口需要声明地理位置用途而且用户必须手动授权。如果用户拒绝授权就无法定位打卡功能直接不可用所以我做了权限检查提示用户去设置页重新授权。精度问题上GPS在室内或高楼附近漂移明显实测在工地办公室里坐标可能飘到几百米外。我的策略是允许管理员在后台手工修正打卡记录同时把打卡范围扩到300米范围降低误判率。如果公司对打卡地点要求非常严格可以配合WiFi签到或蓝牙Beacon方案但那种情况下就要超出小程序范畴了需要额外的硬件支持。7. 经验扩展与后期优化建议7.1 从考勤记录到工资核算的联动目前这个系统只记录了考勤数据没有和工资计算打通。实际上建筑公司的工资结构往往是底薪加日薪请假扣款、加班补贴都直接和考勤明细挂钩。我规划中的下一步是增加一个薪资汇总表后端根据每个月的请假记录和考勤天数自动生成工资明细然后导出Excel给财务。这里涉及到一个计算逻辑假设员工当月应出勤天数为22天实际出勤天数为20天按照日薪300元结算请假两天扣除600元。如果事假和病假的扣款比例不同请假类型字段就发挥作用了。这块功能不难但能把系统价值提升一个档次管理者月底不用把数据挨个复制到Excel里。7.2 管理员后台的Web端扩展目前管理员审批必须在微信小程序端操作体验还凑合但审批列表一多就不太方便了。后续考虑加一个独立的Web后台用Vue加Element Admin实现Flask端提供管理接口权限要区分严格一些。管理后台能做更多事比如批量导入员工、按项目筛选考勤数据、生成多维度的出勤率报表。我在设计Flask接口时已经预留了role字段管理员接口用装饰器做了权限拦截所以Web后台的接口完全可以直接复用只需要前端重新写一套页面。这个扩展方向对于想要做成独立产品的团队很有参考价值。7.3 消息通知与数据安全消息服务建议接入微信的订阅消息员工请假审批通过或驳回后给员工发送一条订阅消息提醒。但微信订阅消息现在是一次授权只能发一次所以要在提交请假时让用户主动订阅否则后续发不出去。数据安全方面考勤数据属于员工隐私后端日志中不能明文打印经纬度和手机号我把坐标直接在应用层做了脱敏处理只保留位置名称信息定位经纬度不入日志。数据库也会定期备份用crontab每天凌晨备份一次防止服务器宕机导致考勤数据丢失。这些细节虽小但要等到出问题再补就会被动了。这篇文章我尽量把整个系统的设计和开发过程讲透了从数据库表设计到小程序交互细节再到部署运维中的各种坑都是我实际做了一遍之后总结出来的经验。如果你也在计划做类似的考勤系统最快的小路是先跑通Flask后端这几个接口然后用微信开发者工具直接打开小程序目录联调前端页面不求好看但求功能正确等整体业务跑顺了再慢慢优化界面和交互体验。拿这套方案作为骨架往里面添加会议室预约、加班申请、电子围栏等功能也都很方便一次基础架构打牢后续扩展就是加表和加接口的事了。
返回列表