package com.xiaopeng.maintenance.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.feignClient.account.XpOperationUserClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient; import com.xiaopeng.api.model.account.SysRole; import com.xiaopeng.api.model.account.XpOperationsRole;import com.xiaopeng.api.model.account.XpOperationsUser;import com.xiaopeng.api.model.maintenance.XpCheckLocation; import com.xiaopeng.api.model.maintenance.XpClockIn; import com.xiaopeng.api.model.maintenance.XpSchedulingDetail; import com.xiaopeng.api.model.maintenance.XpShift; import com.xiaopeng.api.vo.account.XpOperationsUserVO; import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO;import com.xiaopeng.common.core.constant.Constants; import com.xiaopeng.common.core.domain.R; import com.xiaopeng.common.core.utils.DateUtils; import com.xiaopeng.common.core.utils.GeodesyUtil; import com.xiaopeng.common.core.utils.StringUtils; import com.xiaopeng.common.core.utils.poi.ExcelUtil; import com.xiaopeng.common.core.web.page.PageInfo; import com.xiaopeng.maintenance.dto.*; import com.xiaopeng.maintenance.mapper.XpCheckLocationMapper; import com.xiaopeng.maintenance.mapper.XpClockInMapper; import com.xiaopeng.maintenance.mapper.XpShiftMapper; import com.xiaopeng.maintenance.service.IXpClockInService; import com.xiaopeng.maintenance.service.IXpSchedulingDetailService; import com.xiaopeng.maintenance.vo.ClockInStatisticsDetailsVo; import com.xiaopeng.maintenance.vo.ClockInStatisticsVo; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import lombok.extern.slf4j.Slf4j; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; /** *

* 打卡明细 服务实现类 *

* * @author mitao * @since 2026-02-03 */ @Slf4j @Service public class XpClockInServiceImpl extends ServiceImpl implements IXpClockInService { @Resource private XpCheckLocationMapper xpCheckLocationMapper; @Resource private XpShiftMapper xpShiftMapper; @Resource private XpClockInMapper xpClockInMapper; @Resource private XpOperationUserClient xpOperationUserClient; @Resource private IXpSchedulingDetailService xpSchedulingDetailService; @Resource private XpFleetOperationsClient fleetOperationsClient; @Override public Boolean verifyRange(VerifyRangeDTO dto) { XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId()); Map distance = GeodesyUtil.getDistance(dto.getUserLon() + "," + dto.getUserLat(), xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat()); if (null != distance) { Double distance1 = distance.get("WGS84"); if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) { return false; } else { return true; } } else { log.error("调用高德计算距离出错"); return false; } } @Override public R clockIn(ClockDTO dto) { XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId()); if (null == xpCheckLocation) { return R.fail("打卡地址不存在"); } if (xpCheckLocation.getStatus() == 0) { return R.fail("打卡地址已禁用"); } Map distance = GeodesyUtil.getDistance(dto.getUserLon() + "," + dto.getUserLat(), xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat()); if (null != distance) { Double distance1 = distance.get("WGS84"); if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) { return R.fail("不在打卡范围内"); } } else { return R.fail("打卡失败"); } // 查询班次 XpShift xpShift = xpShiftMapper.selectById(dto.getShiftId()); if (null == xpShift) { return R.fail("班次不存在"); } // 开始打卡时间 String startTime = xpShift.getStartTime(); LocalDateTime startLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + startTime + ":00"); XpClockIn xpClockIn = new XpClockIn(); xpClockIn.setAppUserId(dto.getOperationsUserId()); xpClockIn.setClockInTime(LocalDateTime.now()); xpClockIn.setClockInLon(dto.getUserLon()); xpClockIn.setClockInLat(dto.getUserLat()); xpClockIn.setShiftId(dto.getShiftId()); xpClockIn.setClockInAddress(dto.getClockInAddress()); if (xpClockIn.getClockInTime().isAfter(startLocalDateTime)) { xpClockIn.setClockInResult(2); } else { xpClockIn.setClockInResult(1); } this.save(xpClockIn); return R.ok(); } @Override public R clockOut(ClockDTO dto) { XpCheckLocation xpCheckLocation = xpCheckLocationMapper.selectById(dto.getLocationId()); if (null == xpCheckLocation) { return R.fail("打卡地址不存在"); } if (xpCheckLocation.getStatus() == 0) { return R.fail("打卡地址已禁用"); } Map distance = GeodesyUtil.getDistance(dto.getUserLon() + "," + dto.getUserLat(), xpCheckLocation.getLocationLon() + "," + xpCheckLocation.getLocationLat()); if (null != distance) { Double distance1 = distance.get("WGS84"); if (BigDecimal.valueOf(distance1).compareTo(xpCheckLocation.getLocationRange()) > 0) { return R.fail("不在打卡范围内"); } } else { return R.fail("打卡失败"); } // 查询上班打卡信息 XpClockIn xpClockIn = xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class) .eq(XpClockIn::getAppUserId, dto.getOperationsUserId()) .likeRight(XpClockIn::getClockInTime, LocalDate.now()) .last("LIMIT 1")); if (Objects.isNull(xpClockIn)) { return R.fail("请先上班打卡"); } // 查询班次 XpShift xpShift = xpShiftMapper.selectById(xpClockIn.getShiftId()); if (null == xpShift) { return R.fail("班次不存在"); } // 开始打卡时间 String endTime = xpShift.getEndTime(); LocalDateTime endLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + endTime + ":00"); xpClockIn.setClockOutTime(LocalDateTime.now()); xpClockIn.setClockOutLon(dto.getUserLon()); xpClockIn.setClockOutLat(dto.getUserLat()); xpClockIn.setClockOutAddress(dto.getClockOutAddress()); if (xpClockIn.getClockOutTime().isBefore(endLocalDateTime)) { xpClockIn.setClockOutResult(2); } else { xpClockIn.setClockOutResult(1); } xpClockIn.setAppUserId(null); this.updateById(xpClockIn); return R.ok(); } @Override public Integer checkWorkStatus(Long appUserId) { if (appUserId == null) { log.warn("检查上班状态失败: appUserId 为空"); return 1; } try { // 1. 计算当天时间范围 LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN); // 00:00:00 LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX); // 23:59:59 // 2. 查询当天打卡记录 XpClockIn xpClockIn = baseMapper.selectOne(new LambdaQueryWrapper() .eq(XpClockIn::getAppUserId, appUserId) .between(XpClockIn::getClockInTime, startTime, endTime) .last("limit 1")); // 3. 判断上班状态 // 有上班打卡状态,无下班打卡状态 if (Objects.isNull(xpClockIn)) { return 1; } else if (Objects.nonNull(xpClockIn.getClockInResult()) && Objects.isNull(xpClockIn.getClockOutResult())) { return 2; } else { return 3; } } catch (Exception e) { log.error("检查用户上班状态异常, appUserId={}", appUserId, e); // 异常情况返回 false return 1; } } /** * 获取打卡统计 * * @param query * @return */ @Override public PageInfo getClockInStatistics(ClockInStatisticsVo query) { PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); List appUserIds1 = new ArrayList<>(); List appUserIds2 = new ArrayList<>(); List appUserIds3 = new ArrayList<>(); //处理查询条件 if(StringUtils.isNotEmpty(query.getName())){ R> listR = xpOperationUserClient.listByName(query.getName()); if(listR.getCode() == Constants.SUCCESS){ List collect = listR.getData().stream().map(XpOperationsUser::getId).collect(Collectors.toList()); appUserIds1.addAll(collect); } appUserIds1.add(0L); } if(null != query.getRoleId()){ R> roleByRoleId = xpOperationUserClient.getXpOperationsRoleByRoleId(query.getRoleId()); if(roleByRoleId.getCode() == Constants.SUCCESS){ List collect = roleByRoleId.getData().stream().map(XpOperationsRole::getOperationsUserId).collect(Collectors.toList()); appUserIds2.addAll(collect); } appUserIds2.add(0L); } if(StringUtils.isNotEmpty(query.getName()) && null != query.getRoleId()){ for (Long l : appUserIds1) { if(appUserIds2.contains(l)){ appUserIds3.add(l); } } }else{ appUserIds3.addAll(appUserIds1); appUserIds3.addAll(appUserIds2); } String startTime = ""; String endTime = ""; if(StringUtils.isNotEmpty(query.getStatisticDate())){ String statisticDate = query.getStatisticDate(); startTime = statisticDate.split(" - ")[0].split(" ")[0]; endTime = statisticDate.split(" - ")[1].split(" ")[0]; } //查询数据 List clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds3, startTime, endTime); List appUserIdList = clockInStatistics.stream().map(ClockInStatisticsDto::getAppUserId).collect(Collectors.toList()); List ids = appUserIdList.stream().map(Long::parseLong).collect(Collectors.toList()); List data = xpOperationUserClient.listByIdsWithRole(ids).getData(); Map userMap = data.stream().collect(Collectors.toMap(XpOperationsUserWithRoleVO::getId, Function.identity())); //将查询结果组装成map对象,用于循环中提取数据 for (ClockInStatisticsDto clockInStatistic : clockInStatistics) { String appUserId = clockInStatistic.getAppUserId(); //运维账号可以拥有多个角色 if(CollUtil.isNotEmpty(userMap)){ XpOperationsUserWithRoleVO vo = userMap.get(Long.parseLong(appUserId)); if (Objects.nonNull(vo)) { clockInStatistic.setName(vo.getNickName()); String roleName = vo.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";")); clockInStatistic.setRoleName(roleName); } } } pageInfo.setRecords(clockInStatistics); return pageInfo; } /** * 导出打卡统计 * @param query * @param response */ @Override public void exportClockInStatistics(ClockInStatisticsVo query, HttpServletResponse response) { PageInfo pageInfo = new PageInfo<>(0, 10000); List appUserIds1 = new ArrayList<>(); List appUserIds2 = new ArrayList<>(); List appUserIds3 = new ArrayList<>(); //处理查询条件 if(StringUtils.isNotEmpty(query.getName())){ R> listR = xpOperationUserClient.listByName(query.getName()); if(listR.getCode() == Constants.SUCCESS){ List collect = listR.getData().stream().map(XpOperationsUser::getId).collect(Collectors.toList()); appUserIds1.addAll(collect); } appUserIds1.add(0L); } if(null != query.getRoleId()){ R> roleByRoleId = xpOperationUserClient.getXpOperationsRoleByRoleId(query.getRoleId()); if(roleByRoleId.getCode() == Constants.SUCCESS){ List collect = roleByRoleId.getData().stream().map(XpOperationsRole::getOperationsUserId).collect(Collectors.toList()); appUserIds2.addAll(collect); } appUserIds2.add(0L); } if(StringUtils.isNotEmpty(query.getName()) && null != query.getRoleId()){ for (Long l : appUserIds1) { if(appUserIds2.contains(l)){ appUserIds3.add(l); } } }else{ appUserIds3.addAll(appUserIds1); appUserIds3.addAll(appUserIds2); } String startTime = ""; String endTime = ""; if(StringUtils.isNotEmpty(query.getStatisticDate())){ String statisticDate = query.getStatisticDate(); startTime = statisticDate.split(" - ")[0].split(" ")[0]; endTime = statisticDate.split(" - ")[1].split(" ")[0]; } //查询数据 List clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds3, startTime, endTime); List appUserIdList = clockInStatistics.stream().map(ClockInStatisticsDto::getAppUserId).collect(Collectors.toList()); List ids = appUserIdList.stream().map(Long::parseLong).collect(Collectors.toList()); List data = xpOperationUserClient.listByIdsWithRole(ids).getData(); Map userMap = data.stream().collect(Collectors.toMap(XpOperationsUserWithRoleVO::getId, Function.identity())); for (ClockInStatisticsDto clockInStatistic : clockInStatistics) { String appUserId = clockInStatistic.getAppUserId(); //运维账号可以拥有多个角色 if(CollUtil.isNotEmpty(userMap)){ XpOperationsUserWithRoleVO vo = userMap.get(Long.parseLong(appUserId)); if (Objects.nonNull(vo)) { clockInStatistic.setName(vo.getNickName()); String roleName = vo.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";")); clockInStatistic.setRoleName(roleName); } } } ExcelUtil excel = new ExcelUtil(ClockInStatisticsDto.class); excel.exportExcel(response, clockInStatistics, "打卡统计"); } /** * 获取打卡统计明细 * @param query * @return */ @Override public GetClockInStatisticsDetailsDto getClockInStatisticsDetails(ClockInStatisticsDetailsVo query) { GetClockInStatisticsDetailsDto dto = new GetClockInStatisticsDetailsDto(); PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); String startTime = ""; String endTime = ""; if(StringUtils.isNotEmpty(query.getStatisticDate())){ String statisticDate = query.getStatisticDate(); startTime = statisticDate.split(" - ")[0].split(" ")[0]; endTime = statisticDate.split(" - ")[1].split(" ")[0]; } List clockInStatisticsDetails = this.baseMapper.getClockInStatisticsDetails(pageInfo, query.getAppUserId(), startTime, endTime); List data = xpOperationUserClient.listByIdsWithRole(Arrays.asList(query.getAppUserId())).getData(); XpOperationsUserWithRoleVO xpOperationsUserWithRoleVO = data.get(0); //将查询结果组装成map对象,用于循环中提取数据 for (ClockInStatisticsDetailsDto clockInStatistic : clockInStatisticsDetails) { clockInStatistic.setName(xpOperationsUserWithRoleVO.getNickName()); clockInStatistic.setRoleName(xpOperationsUserWithRoleVO.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";"))); } pageInfo.setRecords(clockInStatisticsDetails); dto.setList(pageInfo); XpOperationsUser xpOperationsUser = xpOperationUserClient.queryById(query.getAppUserId()).getData(); List longList = fleetOperationsClient.getFleetIdsByUserId(xpOperationsUser.getId()).getData(); List list = xpSchedulingDetailService.list(new QueryWrapper().eq("fleet_id", longList.get(0)).between(StringUtils.isNotEmpty(startTime), "plan_time", startTime, endTime)); dto.setShouldClockInCount(list.size() * 2); dto.setNormalClockInCount(this.baseMapper.getClockInStatisticsDetailsCount(1, query.getAppUserId(), startTime, endTime)); dto.setLateClockInCount(this.baseMapper.getClockInStatisticsDetailsCount(2, query.getAppUserId(), startTime, endTime)); dto.setMissingClockInCount(this.baseMapper.getClockInStatisticsDetailsCount(3, query.getAppUserId(), startTime, endTime)); return dto; } /** * 导出打卡统计明细 * @param query * @param response */ @Override public void exportClockInStatisticsDetails(ClockInStatisticsDetailsVo query, HttpServletResponse response) { PageInfo pageInfo = new PageInfo<>(0, 10000); String startTime = ""; String endTime = ""; if(StringUtils.isNotEmpty(query.getStatisticDate())){ String statisticDate = query.getStatisticDate(); startTime = statisticDate.split(" - ")[0].split(" ")[0]; endTime = statisticDate.split(" - ")[1].split(" ")[0]; } List clockInStatisticsDetails = this.baseMapper.getClockInStatisticsDetails(pageInfo, query.getAppUserId(), startTime, endTime); List data = xpOperationUserClient.listByIdsWithRole(Arrays.asList(query.getAppUserId())).getData(); XpOperationsUserWithRoleVO xpOperationsUserWithRoleVO = data.get(0); //将查询结果组装成map对象,用于循环中提取数据 for (ClockInStatisticsDetailsDto clockInStatistic : clockInStatisticsDetails) { clockInStatistic.setName(xpOperationsUserWithRoleVO.getNickName()); clockInStatistic.setRoleName(xpOperationsUserWithRoleVO.getRoles().stream().map(SysRole::getRoleName).collect(Collectors.joining(";"))); } ExcelUtil excel = new ExcelUtil(ClockInStatisticsDetailsDto.class); excel.exportExcel(response, clockInStatisticsDetails, "打卡统计明细"); } }