概述
考勤系统是企业/学校常见需求。App Inventor 2可以实现的考勤方案:
| 方案 | 原理 | 精度 | 难度 |
|---|---|---|---|
| GPS定位打卡 | LocationSensor定位 | 10-50米 | ⭐⭐ |
| 二维码签到 | 扫码确认身份 | 高 | ⭐⭐ |
| WiFi打卡 | 连接指定WiFi | 中 | ⭐⭐⭐ |
| 蓝牙打卡 | 检测蓝牙信标 | 高 | ⭐⭐⭐ |
方案一:GPS定位打卡
原理
员工到达公司附近,点击”打卡”按钮。App获取当前GPS位置,与公司坐标比较,在范围内则打卡成功。
界面设计
Screen_Attendance
├── 标签_日期时间(Clock实时更新)
├── 标签_位置(显示经纬度)
├── 标签_距离(距公司多远)
├── 标签_状态(打卡状态)
├── 按钮_打卡
├── ListView_记录(打卡记录)
├── LocationSensor1
├── Clock1
├── TinyDB1
└── Web1(上传到服务器)
积木块代码
设 公司纬度 = 39.9042
设 公司经度 = 116.4074
设 打卡范围 = 200 ' 200米内可以打卡
设 当前纬度 = 0
设 当前经度 = 0
当 Screen_Attendance.初始化
设 LocationSensor1.启用 = true
设 Clock1.计时器间隔 = 1000
设 Clock1.启用计时器 = true
标签_日期时间.文本 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyy-MM-dd HH:mm:ss")
当 Clock1.计时
标签_日期时间.文本 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyy-MM-dd HH:mm:ss")
当 LocationSensor1.位置改变(纬度, 经度, 海拔, 速度)
设 当前纬度 = 纬度
设 当前经度 = 经度
标签_位置.文本 = "📍 " & 数学.保留小数(纬度, 6) & ", " & 数学.保留小数(经度, 6)
' 计算距离
设 距离 = 计算距离(纬度, 经度, 公司纬度, 公司经度)
标签_距离.文本 = "距公司:" & 数学.保留小数(距离, 0) & "米"
当 按钮_打卡.被点击
如果 当前纬度 = 0
标签_状态.文本 = "❌ GPS定位中,请稍候..."
返回
设 距离 = 计算距离(当前纬度, 当前经度, 公司纬度, 公司经度)
设 当前时间 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyy-MM-dd HH:mm:ss")
设 今天日期 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyy-MM-dd")
如果 距离 ≤ 打卡范围
' 在打卡范围内
设 记录 = 创建字典(
"date" → 今天日期,
"time" → 当前时间,
"type" → "签到",
"lat" → 当前纬度,
"lng" → 当前经度,
"distance" → 数学.保留小数(距离, 0)
)
' 保存到本地
设 所有记录 = TinyDB1.获取值("attendance_records", 创建空列表)
设 所有记录 = 添加列表项(所有记录, 记录)
调用 TinyDB1.存储值("attendance_records", 所有记录)
' 上传到服务器
设 JSON = 调用 文本.转JSON(记录)
设 Web1.请求头 = ["Content-Type: application/json"]
调用 Web1.执行Post文本请求("https://api.example.com/attendance", JSON, "UTF-8")
标签_状态.文本 = "✅ 打卡成功!" & 当前时间
标签_状态.文本颜色 = 绿色
调用 刷新记录列表()
否则
标签_状态.文本 = "❌ 不在打卡范围内(距公司" & 数学.保留小数(距离, 0) & "米)"
标签_状态.文本颜色 = 红色
Haversine距离计算
定义 计算距离(纬度1, 经度1, 纬度2, 经度2) 返回 距离米
设 R = 6371000
设 dLat = (纬度2 - 纬度1) * 数学.PI / 180
设 dLon = (经度2 - 经度1) * 数学.PI / 180
设 a = 数学.正弦(dLat/2) * 数学.正弦(dLat/2) +
数学.余弦(纬度1 * 数学.PI / 180) * 数学.余弦(纬度2 * 数学.PI / 180) *
数学.正弦(dLon/2) * 数学.正弦(dLon/2)
设 c = 2 * 数学.反正切2(数学.平方根(a), 数学.平方根(1-a))
返回 R * c
方案二:二维码签到
原理
管理员生成每日动态二维码,员工扫码签到。二维码内容包含日期和随机验证码,防止截图作弊。
生成签到二维码
已有QRCodeGenerator扩展(extensions/QRCodeGenerator.md)。
当 按钮_生成签到码.被点击
设 今天 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyyMMdd")
设 随机码 = 文本转十六进制(数学.随机整数(100000, 999999))
设 签到码 = "ATTENDANCE:" & 今天 & ":" & 随机码
调用 QRCodeGenerator1.生成二维码(签到码, 400)
扫码签到
使用BarcodeScanner组件:
当 按钮_扫码签到.被点击
调用 条码扫描器1.扫描()
当 条码扫描器1.扫描完成(结果, 格式)
如果 结果 以 "ATTENDANCE:" 开头
设 部分 = 分割文本(结果, ":")
设 日期部分 = 列表第2项(部分)
设 今天 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyyMMdd")
如果 日期部分 = 今天
' 验证通过
设 记录 = 创建字典(
"date" → 今天,
"time" → Clock1.格式化日期时间(Clock1.当前时间(), "HH:mm:ss"),
"code" → 列表第3项(部分)
)
调用 保存记录(记录)
标签_状态.文本 = "✅ 签到成功!"
否则
标签_状态.文本 = "❌ 二维码已过期"
否则
标签_状态.文本 = "❌ 无效的签到二维码"
防作弊机制
1. 位置模拟检测
当 LocationSensor1.位置改变(纬度, 经度, 海拔, 速度)
' 检测GPS模拟
如果 LocationSensor1.所有提供者 = "gps" 且 LocationSensor1.提供者 = "fake"
标签_状态.文本 = "⚠️ 检测到位置模拟"
2. 动态二维码(每分钟更换)
设 当前验证码 = ""
当 Clock_验证码.计时
设 分钟 = Clock1.分(Clock1.当前时间())
设 小时 = Clock1.时(Clock1.当前时间())
设 种子 = 小时 * 60 + 分钟
设 当前验证码 = "ATT:" & Clock1.格式化日期时间(Clock1.当前时间(), "yyyyMMdd") & ":" & 文本转十六进制(种子 * 37 + 42)
3. WiFi辅助验证
当 按钮_打卡.被点击
设 连接WiFi = 获取当前WiFi名称() ' 需要扩展
如果 连接WiFi = "Company_WiFi"
' WiFi验证通过,允许打卡
否则
标签_状态.文本 = "❌ 请连接公司WiFi"
数据管理
本地存储结构
TinyDB键: "attendance_records"
值: [
{"date": "2026-05-18", "time": "08:55:30", "type": "签到", "lat": 39.904, "lng": 116.407},
{"date": "2026-05-18", "time": "18:05:12", "type": "签退", "lat": 39.904, "lng": 116.407},
{"date": "2026-05-17", "time": "08:58:45", "type": "签到", "lat": 39.904, "lng": 116.407}
]
上传到服务器
已有api_communication.md和upload_file.md的Web组件用法,此处整合:
定义 上传记录(记录)
设 JSON = 调用 文本.转JSON(记录)
设 请求头 = [
"Content-Type: application/json",
"Authorization: Bearer " & 用户令牌
]
设 Web1.请求头 = 请求头
调用 Web1.执行Post文本请求("https://api.example.com/api/attendance", JSON, "UTF-8")
查看打卡记录
定义 刷新记录列表()
设 所有记录 = TinyDB1.获取值("attendance_records", 创建空列表)
设 显示列表 = 创建空列表
设 今天 = Clock1.格式化日期时间(Clock1.当前时间(), "yyyy-MM-dd")
设 i = 列表长度(所有记录)
当 i ≥ 1 ' 倒序显示
设 记录 = 列表第i项(所有记录)
如果 从字典 记录 获取 "date" = 今天
设 时间 = 从字典 记录 获取 "time"
设 类型 = 从字典 记录 获取 "type"
设 显示列表 = 添加列表项(显示列表, 类型 & " " & 时间)
设 i = i - 1
设 ListView_记录.元素 = 显示列表
与已有文档的关系
本文整合了以下已有文档中的技术方案,此处只补充考勤场景的具体整合用法:
| 技术 | 已有文档 | 本文用途 |
|---|---|---|
| GPS定位 | sensors.md |
定位打卡 |
| 距离计算 | map_geofence.md |
判断是否在范围内 |
| 二维码 | extensions/QRCodeGenerator.md |
二维码签到 |
| Web API | api_communication.md |
上传打卡记录 |
| TinyDB | db.md |
本地记录存储 |
| 加密 | text_encryption.md |
防作弊验证码 |
| 登录 | login_register.md |
员工身份认证 |
| 时间处理 | timestamp_clock.md |
打卡时间 |
常见问题
Q1: GPS定位精度不够?
- 增大打卡范围(如200-500米)
- 结合WiFi验证
- 使用高精度定位模式
- LocationSensor的
ProviderName设为 “gps”
Q2: 室内GPS信号弱?
- 结合WiFi辅助判断
- 使用蓝牙信标(需要扩展)
- 允许手动输入+管理员确认
Q3: 如何防止代打卡?
- 人脸识别(需要AI扩展)
- 动态二维码+短时效
- 设备指纹(需扩展)
- WiFi+GPS双重验证
总结
| 方案 | 安全性 | 成本 | 适用场景 |
|---|---|---|---|
| GPS打卡 | ⭐⭐ | 低 | 室外/大范围 |
| 二维码签到 | ⭐⭐⭐ | 低 | 室内教室 |
| WiFi打卡 | ⭐⭐⭐ | 低 | 办公室 |
| 蓝牙打卡 | ⭐⭐⭐⭐ | 中 | 精确场景 |
版权声明:MIT App Inventor 官方文档采用 CC BY-SA 4.0 授权,本文档由 ai2claw 🐝 整理。
扫码添加客服咨询