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

React Native考勤应用位置校验失效:非指定区域仍可打卡

问题修复:React Native考勤APP位置校验失效问题

核心问题分析

  1. 错误的距离判断逻辑:当前用经纬度直接加减1来判断是否在1米范围内,这完全错误——经纬度的1度约等于111公里,这种判断会导致几乎所有位置都被判定为“匹配”。
  2. 签退无位置校验:签退操作直接执行状态更新,没有检查用户是否在指定位置,导致任意位置都能签退。
  3. watchPosition使用不当:签到时用watchPosition持续监听位置,即使第一次不匹配,后续位置变动可能会意外触发状态更新,且未正确管理监听ID。
  4. 日期比较逻辑错误:直接用Date对象比较是否为同一天,由于对象引用不同,永远会返回false,导致每日状态重置逻辑失效。

解决方案

1. 实现正确的距离计算函数(Haversine公式)

Haversine公式用于计算地球上两点间的球面距离,能准确得到米级的距离:

// 计算两点间距离(单位:米)
const calculateDistance = (lat1, lon1, lat2, lon2) => {
  const R = 6371000; // 地球半径(米)
  const dLat = (lat2 - lat1) * Math.PI / 180;
  const dLon = (lon2 - lon1) * Math.PI / 180;
  const a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * 
    Math.sin(dLon/2) * Math.sin(dLon/2); 
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  return R * c; // 返回距离(米)
};

2. 修复签到函数

改用getCurrentPosition单次获取位置,用正确的距离判断,且仅在匹配时更新状态:

const distanceThreshold = 1; // 1米阈值

const checkIn = () => {
  console.log('Check In');
  Geolocation.getCurrentPosition(
    position => {
      const currentLat = position.coords.latitude;
      const currentLon = position.coords.longitude;
      const distance = calculateDistance(currentLat, currentLon, User.latitude, User.longitude);

      if (distance <= distanceThreshold) {
        setCurrentLoc({ latitude: currentLat, longitude: currentLon });
        setMessage('Matched');
        console.log('Location matched, checking in');
        setStatus('In');
        AsyncStorage.setItem(
          '@status',
          JSON.stringify({ status: 'In', date: new Date().toISOString().split('T')[0] }) // 仅存日期部分
        );
      } else {
        setMessage('NotMatched');
        console.log(`Location not matched, distance: ${distance.toFixed(2)}m`);
      }
    },
    error => {
      console.error('Geolocation error:', error);
      setMessage('NotMatched');
    },
    {
      enableHighAccuracy: true, // 启用高精度定位,提升准确性
      timeout: 10000,
      maximumAge: 0
    }
  );
};

3. 修复签退函数

添加位置校验,仅在指定位置内允许签退:

const checkOut = () => {
  Geolocation.getCurrentPosition(
    position => {
      const currentLat = position.coords.latitude;
      const currentLon = position.coords.longitude;
      const distance = calculateDistance(currentLat, currentLon, User.latitude, User.longitude);

      if (distance <= distanceThreshold) {
        AsyncStorage.getItem('@status').then(status => {
          if (status) {
            const parsedStatus = JSON.parse(status);
            AsyncStorage.setItem(
              '@UserLog',
              JSON.stringify({
                name: User.name,
                INstatus: parsedStatus.status,
                INdate: parsedStatus.date,
                OUTstatus: 'Out',
                OUTdate: new Date().toISOString().split('T')[0]
              })
            );
          }
        });
        setStatus('AlreadyOut');
        AsyncStorage.setItem(
          '@status',
          JSON.stringify({ status: 'AlreadyOut', date: new Date().toISOString().split('T')[0] })
        );
        Alert.alert('Check Out', 'You are checked out');
      } else {
        Alert.alert('Error', 'You are not at the designated location to check out');
        setMessage('NotMatched');
      }
    },
    error => {
      console.error('Geolocation error:', error);
      Alert.alert('Error', 'Failed to get location');
    },
    {
      enableHighAccuracy: true,
      timeout: 10000,
      maximumAge: 0
    }
  );
};

4. 修复日期比较逻辑

在useEffect中比较日期字符串,而不是Date对象:

useEffect(() => {
  AsyncStorage.getItem('user').then(user => {
    if (user) {
      setUser(JSON.parse(user));
    }
  });

  AsyncStorage.getItem('@status').then(status => {
    if (status) {
      const parsedStatus = JSON.parse(status);
      const today = new Date().toISOString().split('T')[0];
      if (parsedStatus.date !== today) {
        // 不是今天,重置状态为Out
        const newStatus = JSON.stringify({ status: 'Out', date: today });
        AsyncStorage.setItem('@status', newStatus);
        setStatus('Out');
      } else {
        setStatus(parsedStatus.status);
      }
    } else {
      // 无状态记录,初始化状态
      const initStatus = JSON.stringify({ status: 'Out', date: new Date().toISOString().split('T')[0] });
      AsyncStorage.setItem('@status', initStatus);
      setStatus('Out');
    }
  });

  // 初始化当前位置
  Geolocation.getCurrentPosition(
    position => {
      setCurrentLoc({
        latitude: position.coords.latitude,
        longitude: position.coords.longitude
      });
    },
    error => console.error('Geolocation error:', error),
    { enableHighAccuracy: true }
  );
}, []);

5. 其他优化点

  • 确保User状态加载完成后再执行位置校验,避免User.latitude/User.longitude为undefined导致计算错误。
  • 给TextInput添加数值校验,确保保存的经纬度是有效数字。

内容的提问来源于stack exchange,提问作者Khuzaima Amir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 04:54:56