package com.xiaopeng.maintenance.service.impl; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.xiaopeng.api.feignClient.account.XpOperationUserClient; 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.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.ClockDTO; import com.xiaopeng.maintenance.dto.ClockInStatisticsDetailsDto; import com.xiaopeng.maintenance.dto.ClockInStatisticsDto; import com.xiaopeng.maintenance.dto.VerifyRangeDTO; 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.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; @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()); 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()); 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.setClockInResult(2); } else { xpClockIn.setClockInResult(1); } xpClockIn.setAppUserId(null); this.updateById(xpClockIn); return R.ok(); } @Override public Boolean checkWorkStatus(Long appUserId) { if (appUserId == null) { log.warn("检查上班状态失败: appUserId 为空"); return false; } 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. 查询当天打卡记录 List records = baseMapper.selectTodayClockInRecords( appUserId, startTime, endTime ); // 3. 判断上班状态 if (records == null || records.isEmpty()) { // 无打卡记录 = 未上班 log.debug("用户 {} 今日无打卡记录, workFlag=false", appUserId); return false; } else if (records.size() == 1) { // 1条记录 = 已上班未下班 log.debug("用户 {} 今日打卡1次, workFlag=true", appUserId); return true; } else { // 2条及以上记录 = 已下班 log.debug("用户 {} 今日打卡{}次, workFlag=false", appUserId, records.size()); return false; } } catch (Exception e) { log.error("检查用户上班状态异常, appUserId={}", appUserId, e); // 异常情况返回 false return false; } } /** * 获取打卡统计 * * @param query * @return */ @Override public PageInfo getClockInStatistics(ClockInStatisticsVo query) { PageInfo pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); List appUserIds = 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()); appUserIds.addAll(collect); } } 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()); appUserIds.addAll(collect); } } String startTime = ""; String endTime = ""; if(StringUtils.isNotEmpty(query.getStatisticDate())){ String statisticDate = query.getStatisticDate(); startTime = statisticDate.split(" - ")[0]; endTime = statisticDate.split(" - ")[1]; } //查询数据 List clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds, 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); } } Integer shouldClockInDays = clockInStatistic.getShouldClockInDays(); Integer clockInDays = clockInStatistic.getClockInDays(); clockInStatistic.setNotClockInDays(shouldClockInDays - clockInDays); } pageInfo.setRecords(clockInStatistics); return pageInfo; } /** * 导出打卡统计 * @param query * @param response */ @Override public void exportClockInStatistics(ClockInStatisticsVo query, HttpServletResponse response) { PageInfo pageInfo = new PageInfo<>(0, 10000); List appUserIds = 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()); appUserIds.addAll(collect); } } 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()); appUserIds.addAll(collect); } } String startTime = ""; String endTime = ""; if(StringUtils.isNotEmpty(query.getStatisticDate())){ String statisticDate = query.getStatisticDate(); startTime = statisticDate.split(" - ")[0]; endTime = statisticDate.split(" - ")[1]; } //查询数据 List clockInStatistics = this.baseMapper.getClockInStatistics(pageInfo, appUserIds, 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); } } Integer shouldClockInDays = clockInStatistic.getShouldClockInDays(); Integer clockInDays = clockInStatistic.getClockInDays(); clockInStatistic.setNotClockInDays(shouldClockInDays - clockInDays); } ExcelUtil excel = new ExcelUtil(ClockInStatisticsDto.class); excel.exportExcel(response, clockInStatistics, "打卡统计"); } /** * 获取打卡统计明细 * @param query * @return */ @Override public PageInfo getClockInStatisticsDetails(ClockInStatisticsDetailsVo query) { 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]; endTime = statisticDate.split(" - ")[1]; } 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); return pageInfo; } /** * 导出打卡统计明细 * @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]; endTime = statisticDate.split(" - ")[1]; } 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, "打卡统计明细"); } }