App Inventor 2 考勤系统教程 - 定位打卡与二维码签到

« 返回首页

概述

考勤系统是企业/学校常见需求。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 🐝 整理。

文档反馈