zss
2 天以前 426ba9ce8ac55c7fe93cb1867c4451d7eb4f96e5
人员打卡签到增加配置
已添加7个文件
已修改5个文件
371 ■■■■ 文件已修改
src/main/java/com/ruoyi/CodeGenerator.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/dto/PersonalAttendanceRecordsDto.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceLocationConfigMapper.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/staff/utils/LocationUtils.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/staff/PersonalAttendanceLocationConfigMapper.xml 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/ruoyi/CodeGenerator.java
@@ -19,11 +19,11 @@
// æ¼”示例子,执行 main æ–¹æ³•控制台输入模块表名回车自动生成对应项目目录中
public class CodeGenerator {
    public static String database_url = "jdbc:mysql://localhost:3306/product-inventory-management-new";
    public static String database_url = "jdbc:mysql://1.15.17.182:9999/product-inventory-management-new";
    public static String database_username = "root";
    public static String database_password= "123456";
    public static String database_password= "xd@123456..";
    public static String author = "芯导软件(江苏)有限公司";
    public static String model = "purchase"; // æ¨¡å—
    public static String model = "staff"; // æ¨¡å—
    public static String setParent = "com.ruoyi."+ model; // åŒ…路径
    public static String tablePrefix = ""; // è®¾ç½®è¿‡æ»¤è¡¨å‰ç¼€
    public static void main(String[] args) {
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceLocationConfigController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package com.ruoyi.staff.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
 * <p>
 * äººå‘˜æ‰“卡规则配置 å‰ç«¯æŽ§åˆ¶å™¨
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-02-11 09:41:34
 */
@RestController
@RequestMapping("/personalAttendanceLocationConfig")
@Api(tags = "人员打卡规则配置")
public class PersonalAttendanceLocationConfigController {
    @Autowired
    private PersonalAttendanceLocationConfigService personalAttendanceLocationConfigService;
    @ApiOperation("新增/修改人员打卡规则配置")
    @PostMapping("/add")
    public R add(@RequestBody PersonalAttendanceLocationConfig personalAttendanceLocationConfig){
        return R.ok(personalAttendanceLocationConfigService.saveOrUpdate(personalAttendanceLocationConfig));
    }
    @ApiOperation("分页查询打卡签到")
    @GetMapping("/listPage")
    public R listPage(Page page){
        return R.ok(personalAttendanceLocationConfigService.page(page));
    }
}
src/main/java/com/ruoyi/staff/controller/PersonalAttendanceRecordsController.java
@@ -6,6 +6,7 @@
import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
import com.ruoyi.staff.service.PersonalAttendanceRecordsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
@@ -21,30 +22,30 @@
 */
@RestController
@RequestMapping("/personalAttendanceRecords")
@Api(tags = "打卡签到")
@Api(tags = "人员打卡签到")
public class PersonalAttendanceRecordsController {
    @Resource
    private PersonalAttendanceRecordsService personalAttendanceRecordsService;
    // æ–°å¢ž
    @ApiOperation("新增打卡签到")
    @PostMapping("")
    public AjaxResult add(@RequestBody PersonalAttendanceRecords personalAttendanceRecord){
        return AjaxResult.success(personalAttendanceRecordsService.add(personalAttendanceRecord));
    public AjaxResult add(@RequestBody PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
        return AjaxResult.success(personalAttendanceRecordsService.add(personalAttendanceRecordsDto));
    }
    // åˆ—表查询
    @ApiOperation("分页查询打卡签到")
    @GetMapping("/listPage")
    public AjaxResult listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
        return AjaxResult.success(personalAttendanceRecordsService.listPage(page, personalAttendanceRecordsDto));
    }
    // ä»Šæ—¥è€ƒå‹¤æ•°æ®
    @ApiOperation("获取当前人的考勤相关数据")
    @GetMapping("/today")
    public AjaxResult todayInfo(PersonalAttendanceRecordsDto personalAttendanceRecordsDto){
        return AjaxResult.success(personalAttendanceRecordsService.todayInfo(personalAttendanceRecordsDto));
    }
    // å¯¼å‡º
    @ApiOperation("导出打卡签到")
    @PostMapping("/export")
    public void export(HttpServletResponse response, PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
        personalAttendanceRecordsService.export(response, personalAttendanceRecordsDto);
src/main/java/com/ruoyi/staff/dto/PersonalAttendanceRecordsDto.java
@@ -1,10 +1,17 @@
package com.ruoyi.staff.dto;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
@Data
@ExcelIgnoreUnannotated
public class PersonalAttendanceRecordsDto extends PersonalAttendanceRecords {
    @Excel(name = "姓名", sort = 3)
    private String staffName;
@@ -16,4 +23,20 @@
    private String deptName;
    private Long deptId;
    //打卡的经度
    private Double longitude;
    //打卡的纬度
    private Double latitude;
    //标准上班时间
    @JsonFormat(pattern = "HH:mm")
    @DateTimeFormat(pattern = "HH:mm")
    private LocalDateTime startAt;
    //标准下班时间
    @JsonFormat(pattern = "HH:mm")
    @DateTimeFormat(pattern = "HH:mm")
    private LocalDateTime endAt;
}
src/main/java/com/ruoyi/staff/mapper/PersonalAttendanceLocationConfigMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package com.ruoyi.staff.mapper;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
 * <p>
 * äººå‘˜æ‰“卡规则配置 Mapper æŽ¥å£
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-02-11 09:41:34
 */
@Mapper
public interface PersonalAttendanceLocationConfigMapper extends BaseMapper<PersonalAttendanceLocationConfig> {
}
src/main/java/com/ruoyi/staff/pojo/PersonalAttendanceLocationConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.staff.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
/**
 * <p>
 * äººå‘˜æ‰“卡规则配置
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-02-11 09:41:34
 */
@Getter
@Setter
@TableName("personal_attendance_location_config")
@ApiModel(value = "PersonalAttendanceLocationConfig对象", description = "人员打卡规则配置")
public class PersonalAttendanceLocationConfig implements Serializable {
    private static final long serialVersionUID = 1L;
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;
    @ApiModelProperty("部门id")
    private Integer sysDeptId;
    @ApiModelProperty("地点名称")
    private String locationName;
    @ApiModelProperty("经度")
    private Double longitude;
    @ApiModelProperty("纬度")
    private Double latitude;
    @ApiModelProperty("打卡范围")
    private Double radius;
    @ApiModelProperty("上班时间")
    @JsonFormat(pattern = "HH:mm")
    @DateTimeFormat(pattern = "HH:mm")
    private LocalDateTime startAt;
    @ApiModelProperty("下班时间")
    @JsonFormat(pattern = "HH:mm")
    @DateTimeFormat(pattern = "HH:mm")
    private LocalDateTime endAt;
}
src/main/java/com/ruoyi/staff/service/PersonalAttendanceLocationConfigService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package com.ruoyi.staff.service;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.baomidou.mybatisplus.extension.service.IService;
/**
 * <p>
 * äººå‘˜æ‰“卡规则配置 æœåŠ¡ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-02-11 09:41:34
 */
public interface PersonalAttendanceLocationConfigService extends IService<PersonalAttendanceLocationConfig> {
}
src/main/java/com/ruoyi/staff/service/PersonalAttendanceRecordsService.java
@@ -21,7 +21,7 @@
public interface PersonalAttendanceRecordsService extends IService<PersonalAttendanceRecords> {
    IPage<PersonalAttendanceRecordsDto> listPage(Page page, PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
    int add(PersonalAttendanceRecords personalAttendanceRecords);
    int add(PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
    PersonalAttendanceRecordsDto todayInfo(PersonalAttendanceRecordsDto personalAttendanceRecordsDto);
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceLocationConfigServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package com.ruoyi.staff.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
import com.ruoyi.staff.service.PersonalAttendanceLocationConfigService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * <p>
 * äººå‘˜æ‰“卡规则配置 æœåŠ¡å®žçŽ°ç±»
 * </p>
 *
 * @author èŠ¯å¯¼è½¯ä»¶ï¼ˆæ±Ÿè‹ï¼‰æœ‰é™å…¬å¸
 * @since 2026-02-11 09:41:34
 */
@Service
public class PersonalAttendanceLocationConfigServiceImpl extends ServiceImpl<PersonalAttendanceLocationConfigMapper, PersonalAttendanceLocationConfig> implements PersonalAttendanceLocationConfigService {
}
src/main/java/com/ruoyi/staff/service/impl/PersonalAttendanceRecordsServiceImpl.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.exception.base.BaseException;
@@ -12,13 +13,16 @@
import com.ruoyi.project.system.service.ISysDictDataService;
import com.ruoyi.staff.dto.PersonalAttendanceRecordsDto;
import com.ruoyi.staff.dto.StaffOnJobDto;
import com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig;
import com.ruoyi.staff.pojo.PersonalAttendanceRecords;
import com.ruoyi.staff.mapper.PersonalAttendanceRecordsMapper;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.staff.service.PersonalAttendanceRecordsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.staff.task.PersonalAttendanceRecordsTask;
import com.ruoyi.staff.utils.LocationUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -41,6 +45,7 @@
 * @since 2026-02-09 01:20:07
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class PersonalAttendanceRecordsServiceImpl extends ServiceImpl<PersonalAttendanceRecordsMapper, PersonalAttendanceRecords> implements PersonalAttendanceRecordsService {
    @Autowired
    private PersonalAttendanceRecordsMapper personalAttendanceRecordsMapper;
@@ -49,7 +54,7 @@
    private StaffOnJobMapper staffOnJobMapper;
    @Autowired
    private PersonalAttendanceRecordsTask personalAttendanceRecordsTask;
    private PersonalAttendanceLocationConfigMapper personalAttendanceLocationConfigMapper;
    @Autowired
    private ISysDictDataService dictDataService;
@@ -58,37 +63,52 @@
    private SysDeptMapper sysDeptMapper;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int add(PersonalAttendanceRecords personalAttendanceRecords) {
    public int add(PersonalAttendanceRecordsDto personalAttendanceRecordsDto) {
        // å½“前时间
        LocalDate currentDate = LocalDate.now();
        // é¦–先根据用户ID查询员工信息
        LocalDateTime currentDateTime = LocalDateTime.now();
        /*查询员工信息*/
        QueryWrapper<StaffOnJob> staffQueryWrapper = new QueryWrapper<>();
        staffQueryWrapper.eq("staff_no", SecurityUtils.getUsername());
        StaffOnJob staffOnJob = staffOnJobMapper.selectOne(staffQueryWrapper);
        if (staffOnJob == null) {
            throw new BaseException("当前用户没有对应的员工信息");
        }
        // å½“前时间
        LocalDateTime currentDateTime = LocalDateTime.now();
        // å¦‚果打卡时间超过考勤下班时间不能打卡
        /*判断打卡位置是否在规则范围内*/
        List<PersonalAttendanceLocationConfig> personalAttendanceLocationConfigs = personalAttendanceLocationConfigMapper.selectList(Wrappers.<PersonalAttendanceLocationConfig>lambdaQuery()
                .eq(PersonalAttendanceLocationConfig::getSysDeptId, staffOnJob.getSysDeptId())
                .orderByDesc(PersonalAttendanceLocationConfig::getId));
        if (personalAttendanceLocationConfigs == null || personalAttendanceLocationConfigs.isEmpty()) {
            throw new BaseException("当前部门没有设置打卡规则");
        }
        Double punchLongitude = personalAttendanceRecordsDto.getLongitude(); //打卡的经度
        Double punchLatitude = personalAttendanceRecordsDto.getLatitude(); // æ‰“卡的纬度
        if (punchLongitude == null || punchLatitude == null) {
            throw new BaseException("打卡失败:未获取到您的位置信息,请开启定位权限");
        }
        //计算打卡位置与考勤点的距离
        PersonalAttendanceLocationConfig locationConfig = personalAttendanceLocationConfigs.get(0);//获取最新的一条数据
        double allowedRadius = locationConfig.getRadius(); // å…è®¸çš„范围(米)
        double actualDistance = LocationUtils.calculateDistance(
                punchLatitude, punchLongitude, // å‘˜å·¥æ‰“卡的经纬度
                locationConfig.getLatitude(), locationConfig.getLongitude() // è€ƒå‹¤ç‚¹çš„经纬度
        );
        //判断是否在范围内
        if (actualDistance > allowedRadius) {
            throw new BaseException(String.format("打卡失败:您当前位置距离考勤点%.2f米,超出允许范围(%s米)", actualDistance, allowedRadius));
        }
        /*判断打卡时间*/
        LocalDateTime  endAt = locationConfig.getEndAt(); //下班时间
        // èŽ·å–è€ƒå‹¤ä¸‹ç­æ—¶é—´ç‚¹
        String[] timeConfigs = getAttendanceTimeConfig();
        String timeConfig = timeConfigs[1];
        String[] timeParts = timeConfig.split(":");
        int standardHour = Integer.parseInt(timeParts[0]);
        int standardMinute = Integer.parseInt(timeParts[1]);
        int standardHour = endAt.getHour();
        int standardMinute = endAt.getMinute();
        // å½“前时间
        int actualHour = currentDateTime.getHour();
        int actualMinute = currentDateTime.getMinute();
        // åˆ¤æ–­æ‰“卡时间是否晚于当前时间
        if (actualHour > standardHour || (actualHour == standardHour && actualMinute > standardMinute)) {
            throw new BaseException("打卡时间不能晚于下班时间");
            throw new BaseException(String.format("打卡失败:打卡时间不能晚于下班时间(%02d:%02d)", standardHour, standardMinute));
        }
        // æ ¹æ®å‘˜å·¥ID和当前日期查询打卡记录
        QueryWrapper<PersonalAttendanceRecords> attendanceQueryWrapper = new QueryWrapper<>();
        attendanceQueryWrapper.eq("staff_on_job_id", staffOnJob.getId())
@@ -97,10 +117,11 @@
        // æ ¹æ®å­—典设置的考勤时间判断迟到早退
        if (attendanceRecord == null) {
            // ä¸å­˜åœ¨æ‰“卡记录,创建新记录
            PersonalAttendanceRecords personalAttendanceRecords = new PersonalAttendanceRecords();
            personalAttendanceRecords.setStaffOnJobId(staffOnJob.getId());
            personalAttendanceRecords.setDate(currentDate);
            personalAttendanceRecords.setWorkStartAt(currentDateTime);
            personalAttendanceRecords.setStatus(determineAttendanceStatus(personalAttendanceRecords, true));
            personalAttendanceRecords.setStatus(determineAttendanceStatus(personalAttendanceRecords, true,locationConfig));
            personalAttendanceRecords.setRemark(personalAttendanceRecords.getRemark());
            personalAttendanceRecords.setTenantId(staffOnJob.getTenantId());
            return personalAttendanceRecordsMapper.insert(personalAttendanceRecords);
@@ -117,7 +138,7 @@
                        .divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP);
                attendanceRecord.setWorkHours(workHours);
                // æ›´æ–°è€ƒå‹¤çŠ¶æ€
                attendanceRecord.setStatus(determineAttendanceStatus(attendanceRecord, false));
                attendanceRecord.setStatus(determineAttendanceStatus(attendanceRecord, false,locationConfig));
                return personalAttendanceRecordsMapper.updateById(attendanceRecord);
            } else {
                throw new BaseException("您已经打过卡了,无需重复打卡!!!");
@@ -125,46 +146,22 @@
        }
    }
    // èŽ·å–è€ƒå‹¤æ—¶é—´é…ç½®
    private String[] getAttendanceTimeConfig() {
        String[] timeConfigs = new String[2];
        try {
            String dictType = "sys_work_time";
            // èŽ·å–ä¸Šç­æ—¶é—´é…ç½®ï¼Œé»˜è®¤ä¸º09:00
            String startTimeConfig = dictDataService.selectDictLabel(dictType, "start_at");
            timeConfigs[0] = (startTimeConfig == null || startTimeConfig.trim().isEmpty()) ? "09:00" : startTimeConfig;
            // èŽ·å–ä¸‹ç­æ—¶é—´é…ç½®ï¼Œé»˜è®¤ä¸º18:00
            String endTimeConfig = dictDataService.selectDictLabel(dictType, "end_at");
            timeConfigs[1] = (endTimeConfig == null || endTimeConfig.trim().isEmpty()) ? "18:00" : endTimeConfig;
            return timeConfigs;
        } catch (Exception e) {
            timeConfigs[0] = "09:00"; // é»˜è®¤ä¸Šç­æ—¶é—´
            timeConfigs[1] = "18:00"; // é»˜è®¤ä¸‹ç­æ—¶é—´
            return timeConfigs;
        }
    }
    // æ ¹æ®å®žé™…时间和是否上班时间判断考勤状态
    // 0 æ­£å¸¸ 1 è¿Ÿåˆ° 2 æ—©é€€ 3 è¿Ÿåˆ°æ—©é€€ 4 ç¼ºå‹¤
    private Integer determineAttendanceStatus(PersonalAttendanceRecords attendanceRecord, boolean isStart) {
    private Integer determineAttendanceStatus(PersonalAttendanceRecords attendanceRecord, boolean isStart,PersonalAttendanceLocationConfig locationConfig) {
        //判断是上班打卡还是下班打卡
        LocalDateTime actualTime = isStart ? attendanceRecord.getWorkStartAt() : attendanceRecord.getWorkEndAt();
        try {
            // èŽ·å–è€ƒå‹¤æ—¶é—´é…ç½®
            String[] timeConfigs = getAttendanceTimeConfig();
            String timeConfig = isStart ? timeConfigs[0] : timeConfigs[1];
            // è§£æžæ ‡å‡†æ—¶é—´
            String[] timeParts = timeConfig.split(":");
            int standardHour = Integer.parseInt(timeParts[0]);
            int standardMinute = Integer.parseInt(timeParts[1]);
            LocalDateTime startAt = locationConfig.getStartAt();//上班时间
            LocalDateTime endAt = locationConfig.getEndAt();//下班时间
            LocalDateTime timeConfig = isStart ? startAt : endAt;
            // è§£æžå°æ—¶å’Œåˆ†é’Ÿ
            int standardHour = timeConfig.getHour();
            int standardMinute = timeConfig.getMinute();
            // èŽ·å–å®žé™…æ—¶é—´çš„æ—¶åˆ†
            int actualHour = actualTime.getHour();
            int actualMinute = actualTime.getMinute();
            // åˆ¤æ–­çŠ¶æ€
            if (isStart) {
                // ä¸Šç­æ‰“卡:超过标准时间算迟到
@@ -180,9 +177,7 @@
                    return 2; // æ—©é€€
                }
            }
            return 0; // æ­£å¸¸
        } catch (Exception e) {
            // å¦‚果获取配置失败,默认返回正常状态
            log.warn("获取考勤时间配置失败,使用默认状态:" + e.getMessage());
@@ -240,7 +235,14 @@
        resultDto.setDeptId(staffOnJob.getSysDeptId() != null ? staffOnJob.getSysDeptId() : null);
        SysDept dept = sysDeptMapper.selectDeptById(staffOnJob.getSysDeptId());
        resultDto.setDeptName(dept != null ? dept.getDeptName() : null);
        //获取该员工对应的打卡规则
        List<PersonalAttendanceLocationConfig> personalAttendanceLocationConfigs = personalAttendanceLocationConfigMapper.selectList(Wrappers.<PersonalAttendanceLocationConfig>lambdaQuery()
                .eq(PersonalAttendanceLocationConfig::getSysDeptId, staffOnJob.getSysDeptId())
                .orderByDesc(PersonalAttendanceLocationConfig::getId));
        if (personalAttendanceLocationConfigs.size()>0){
            resultDto.setStartAt(personalAttendanceLocationConfigs.get(0).getStartAt());
            resultDto.setEndAt(personalAttendanceLocationConfigs.get(0).getEndAt());
        }
        return resultDto;
    }
src/main/java/com/ruoyi/staff/utils/LocationUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package com.ruoyi.staff.utils;
// å·¥å…·ç±»ï¼šè®¡ç®—两个经纬度之间的距离(球面距离)
public class LocationUtils {
    private static final double EARTH_RADIUS = 6371000; // åœ°çƒåŠå¾„,单位米
    /**
     * è®¡ç®—两个经纬度之间的距离(米)
     * @param lat1 ç¬¬ä¸€ä¸ªç‚¹çº¬åº¦
     * @param lon1 ç¬¬ä¸€ä¸ªç‚¹ç»åº¦
     * @param lat2 ç¬¬äºŒä¸ªç‚¹çº¬åº¦
     * @param lon2 ç¬¬äºŒä¸ªç‚¹ç»åº¦
     * @return è·ç¦»ï¼ˆç±³ï¼‰
     */
    public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
        // è½¬å¼§åº¦
        double radLat1 = Math.toRadians(lat1);
        double radLon1 = Math.toRadians(lon1);
        double radLat2 = Math.toRadians(lat2);
        double radLon2 = Math.toRadians(lon2);
        // å·®å€¼
        double deltaLat = radLat1 - radLat2;
        double deltaLon = radLon1 - radLon2;
        // çƒé¢è·ç¦»å…¬å¼
        double distance = 2 * Math.asin(Math.sqrt(
                Math.pow(Math.sin(deltaLat / 2), 2) +
                        Math.cos(radLat1) * Math.cos(radLat2) *
                                Math.pow(Math.sin(deltaLon / 2), 2)
        ));
        distance = distance * EARTH_RADIUS;
        // ä¿ç•™ä¸¤ä½å°æ•°
        distance = Math.round(distance * 100) / 100.0;
        return distance;
    }
}
src/main/resources/mapper/staff/PersonalAttendanceLocationConfigMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.staff.mapper.PersonalAttendanceLocationConfigMapper">
    <!-- é€šç”¨æŸ¥è¯¢æ˜ å°„结果 -->
    <resultMap id="BaseResultMap" type="com.ruoyi.staff.pojo.PersonalAttendanceLocationConfig">
        <id column="id" property="id" />
        <result column="sys_dept_id" property="sysDeptId" />
        <result column="location_name" property="locationName" />
        <result column="longitude" property="longitude" />
        <result column="latitude" property="latitude" />
        <result column="radius" property="radius" />
        <result column="start_at" property="startAt" />
        <result column="end_at" property="endAt" />
    </resultMap>
</mapper>