This commit is contained in:
zt
2025-05-13 16:35:09 +08:00
parent 88cd11228b
commit ee9fd26068
8 changed files with 149 additions and 4 deletions

View File

@ -1,7 +1,9 @@
package com.ruoyi.web.controller.wgz; package com.ruoyi.web.controller.wgz;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.bgt.domain.BgtProjectRecruit; import com.ruoyi.bgt.domain.BgtProjectRecruit;
import com.ruoyi.bgt.domain.BgtProjectRecruitApply; import com.ruoyi.bgt.domain.BgtProjectRecruitApply;
import com.ruoyi.bgt.domain.dto.BgtAttendanceDetailDTO; import com.ruoyi.bgt.domain.dto.BgtAttendanceDetailDTO;
@ -17,6 +19,7 @@
import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.domain.Annex; import com.ruoyi.common.domain.Annex;
import com.ruoyi.common.domain.dto.AnnexDTO; import com.ruoyi.common.domain.dto.AnnexDTO;
import com.ruoyi.common.enums.RecruitApplyStatus;
import com.ruoyi.common.service.IAnnexService; import com.ruoyi.common.service.IAnnexService;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.system.service.ISysDictTypeService; import com.ruoyi.system.service.ISysDictTypeService;
@ -324,6 +327,29 @@
return AjaxResult.success(iWgzAttendanceService.userSubmitTheClock(req)); return AjaxResult.success(iWgzAttendanceService.userSubmitTheClock(req));
} }
@ApiOperation("【考勤打卡】【打卡】 获取打卡范围")
//@PreAuthorize("@ss.hasPermi('wgzApp:user:userSubmitTheClock')")
@GetMapping("/WgzAppUserClockRange")
public AjaxResult<WgzAppUserClockRangeRes> userClockRange() {
WgzAppUserClockRangeRes wgzAppUserClockRangeRes = new WgzAppUserClockRangeRes();
//1、获取当前用户信息
Long appUserId = SecurityUtils.getAppUserId();
LambdaQueryWrapper<BgtProjectRecruitApply> lqw = Wrappers.lambdaQuery();
lqw.eq(BgtProjectRecruitApply::getUserId, appUserId).
eq(BgtProjectRecruitApply::getStatus, RecruitApplyStatus.WORKING.getCode()); //包工头+务工者同意 or 进场 才算进场ok
List<BgtProjectRecruitApply> bgtProjectRecruitApplies = iBgtProjectRecruitApplyService.list(lqw);
if (CollectionUtil.isEmpty(bgtProjectRecruitApplies)) {
return AjaxResult.success(wgzAppUserClockRangeRes);
}
//3、根据工地id得到完整的工地信息
BgtProjectRecruit appById = iBgtProjectRecruitService.getById(bgtProjectRecruitApplies.get(0).getRecruitId());
wgzAppUserClockRangeRes.setCentralLatitude(appById.getCentralLatitude());
wgzAppUserClockRangeRes.setCentralLongitude(appById.getCentralLongitude());
wgzAppUserClockRangeRes.setRangeRadius(appById.getRangeRadius());
return AjaxResult.success(wgzAppUserClockRangeRes);
}
/** /**
* 【考勤打卡】【打卡日历】 打卡日历记录 * 【考勤打卡】【打卡日历】 打卡日历记录
*/ */

View File

@ -54,9 +54,9 @@ public class TokenService {
public LoginUser getLoginUser(HttpServletRequest request) { public LoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌 // 获取请求携带的令牌
String clientIP = ServletUtils.getClientIP();
String token = getToken(request); String token = getToken(request);
// log.info("用户当前token{}", token);
if (Validator.isNotEmpty(token)) { if (Validator.isNotEmpty(token)) {
Claims claims = parseToken(token); Claims claims = parseToken(token);
// 解析对应的权限以及用户信息 // 解析对应的权限以及用户信息
@ -66,8 +66,8 @@ public class TokenService {
String userId = (String) claims.get(Constants.LOGIN_USER_ID); String userId = (String) claims.get(Constants.LOGIN_USER_ID);
String userKey = getTokenKey(type, userId); String userKey = getTokenKey(type, userId);
LoginUser user = redisCache.getCacheObject(userKey); LoginUser user = redisCache.getCacheObject(userKey);
// log.info("用户当前类型:{}", claims);
if(user==null || !uuid.equals(user.getToken())){ if(user==null || !uuid.equals(user.getToken())){
log.info("用户ip:{},用户ID{},用户类型:{},用户当前token{}",clientIP,userId,type,token);
throw new BaseException("999","您的账号在其他设备登录"); throw new BaseException("999","您的账号在其他设备登录");
} }
return user; return user;

View File

@ -140,6 +140,15 @@ public class BgtProjectRecruit implements Serializable {
@ApiModelProperty("下班时间") @ApiModelProperty("下班时间")
private LocalTime endWorkTime; private LocalTime endWorkTime;
@ApiModelProperty("中心点经度")
private Double centralLongitude;
@ApiModelProperty("'中心点纬度'")
private Double centralLatitude;
@ApiModelProperty("'半径'")
private Double rangeRadius;
@ApiModelProperty("工种") @ApiModelProperty("工种")
private String typeOfWork; private String typeOfWork;

View File

@ -119,4 +119,13 @@ public class BgtProjectRecruitDetailVO implements Serializable {
@ApiModelProperty("招工状态(1-进行中2-已招满3-已过期)") @ApiModelProperty("招工状态(1-进行中2-已招满3-已过期)")
private String status; private String status;
@ApiModelProperty("中心点经度")
private Double centralLongitude;
@ApiModelProperty("'中心点纬度'")
private Double centralLatitude;
@ApiModelProperty("'半径'")
private Double rangeRadius;
} }

View File

@ -0,0 +1,68 @@
package com.ruoyi.common.util;
import com.ruoyi.common.exception.BaseException;
public class LocationUtils {
private static final double EARTH_RADIUS = 6371000;
/**
* 计算两点间的球面距离Haversine 公式)
* @throws IllegalArgumentException 如果任何参数为 null
*/
public static double haversine(Double lat1, Double lon1, Double lat2, Double lon2) {
validateCoordinates(lat1, lon1, lat2, lon2);
double lat1Rad = Math.toRadians(lat1);
double lon1Rad = Math.toRadians(lon1);
double lat2Rad = Math.toRadians(lat2);
double lon2Rad = Math.toRadians(lon2);
double dLat = lat2Rad - lat1Rad;
double dLon = lon2Rad - lon1Rad;
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1Rad) * Math.cos(lat2Rad) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return EARTH_RADIUS * c;
}
/**
* 判断当前坐标是否在打卡范围内
* @throws IllegalArgumentException 如果中心点坐标或范围为 null
*/
public static boolean isInRange(Double centerLat, Double centerLon,
Double currentLat, Double currentLon,
Double rangeMeters) {
if (rangeMeters == null || rangeMeters < 0) {
throw new BaseException("打卡范围不能为空且必须为正数");
}
if (currentLat == null || currentLon == null) {
return false; // 当前位置为 null 时视为不在范围内
}
validateCoordinates(centerLat, centerLon);
double distance = haversine(centerLat, centerLon, currentLat, currentLon);
return distance <= rangeMeters;
}
// 参数校验方法
private static void validateCoordinates(Double... coords) {
for (Double coord : coords) {
if (coord == null) {
throw new BaseException("经纬度坐标不能为空");
}
}
}
public static void main(String[] args) {
double haversine = haversine(29.623956, 106.508291, 29.623906, 106.508314);
System.out.println(haversine);
}
}

View File

@ -1,6 +1,5 @@
package com.ruoyi.wgz.bo.req; package com.ruoyi.wgz.bo.req;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
@ -26,4 +25,14 @@ public class WgzAppSubmitTheClockReq implements Serializable {
@ApiModelProperty("打卡位置") @ApiModelProperty("打卡位置")
@NotBlank(message = "打卡位置不能为空") @NotBlank(message = "打卡位置不能为空")
private String pnchOsition; private String pnchOsition;
@ApiModelProperty("当前位置纬度")
@NotNull(message = "当前位置纬度不能为空")
private Double currentLat;
@ApiModelProperty("当前位置经度")
@NotNull(message = "当前位置经度不能为空")
private Double currentLon;
} }

View File

@ -0,0 +1,18 @@
package com.ruoyi.wgz.bo.res;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class WgzAppUserClockRangeRes {
@ApiModelProperty("中心点经度")
private Double centralLongitude;
@ApiModelProperty("'中心点纬度'")
private Double centralLatitude;
@ApiModelProperty("'半径'")
private Double rangeRadius;
}

View File

@ -18,6 +18,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.BaseException; import com.ruoyi.common.exception.BaseException;
import com.ruoyi.common.util.DataUtil; import com.ruoyi.common.util.DataUtil;
import com.ruoyi.common.util.LocalDateToChineseWeekday; import com.ruoyi.common.util.LocalDateToChineseWeekday;
import com.ruoyi.common.util.LocationUtils;
import com.ruoyi.common.utils.DateUtils; import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.PageUtils; import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
@ -172,6 +173,7 @@ public class WgzAttendanceServiceImpl extends ServicePlusImpl<WgzAttendanceMappe
} }
//3、根据工地id得到完整的工地信息 //3、根据工地id得到完整的工地信息
BgtProjectRecruit appById = iBgtProjectRecruitService.getAppById(by.getRecruitId()); BgtProjectRecruit appById = iBgtProjectRecruitService.getAppById(by.getRecruitId());
//4、在进场时间时才能打卡如果有退场记录就不允许打卡 //4、在进场时间时才能打卡如果有退场记录就不允许打卡
if (by.getEntryTime() == null || !by.getStatus().equals("5")) { if (by.getEntryTime() == null || !by.getStatus().equals("5")) {
throw new RuntimeException("需要进场才能实现打卡操作!"); throw new RuntimeException("需要进场才能实现打卡操作!");
@ -179,6 +181,10 @@ public class WgzAttendanceServiceImpl extends ServicePlusImpl<WgzAttendanceMappe
if (by.getLeaveTime() != null) { if (by.getLeaveTime() != null) {
throw new RuntimeException("您已离场,无法进行打卡操作!"); throw new RuntimeException("您已离场,无法进行打卡操作!");
} }
//验证打卡范围
if (!LocationUtils.isInRange(appById.getCentralLatitude(), appById.getCentralLongitude(), req.getCurrentLat(), req.getCurrentLon(), appById.getRangeRadius())) {
throw new RuntimeException("打卡位置不在工地范围内,请重新打卡!");
}
//5、查看当前人、当前工地、当天的打卡记录 //5、查看当前人、当前工地、当天的打卡记录
String formattedDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); String formattedDate = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
WgzAttendance we = publicFindByUserIdWait(appUserId, by.getRecruitId(), formattedDate); WgzAttendance we = publicFindByUserIdWait(appUserId, by.getRecruitId(), formattedDate);