xp-robotaxi-api/src/main/java/com/xiaopeng/api/model/maintenance/XpClockIn.java
@@ -1,8 +1,6 @@ package com.xiaopeng.api.model.maintenance; import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.*; import java.time.LocalDate; import java.time.LocalDateTime; @@ -15,6 +13,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import net.bytebuddy.dynamic.loading.InjectionClassLoader; /** * <p> @@ -37,6 +36,7 @@ private Long id; @ApiModelProperty(value = "用户id") @TableField(updateStrategy = FieldStrategy.NEVER) private Long appUserId; @ApiModelProperty(value = "上班打卡日期") xp-robotaxi-itinerary-boot/src/main/java/com/xiaopeng/itinerary/service/impl/XpPricingServiceImpl.java
@@ -197,12 +197,12 @@ // 1. 基础数据查询 Map<String, String> geocode = gdMapGeocodingUtil.geocode(routeAndFareDto.getOrigin().getLongitude().toString(), routeAndFareDto.getOrigin().getLatitude().toString()); if(null == geocode){ throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND); throw new ServiceException("城市暂未开通服务", I18nKeyConstant.CITY_NOT_FOUND); } Long cityCode = routeAndFareDto.getOpenCityId(); XpOpenCity openCities = xpOpenCityMapper.selectById(cityCode); if (openCities == null ) { throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND); throw new ServiceException("城市暂未开通服务", I18nKeyConstant.CITY_NOT_FOUND); } XpOpenCity xpOpenCity = openCities; routeAndFareDto.setOpenCityId(xpOpenCity.getId()); @@ -553,7 +553,7 @@ XpOpenCity openCities = xpOpenCityMapper.selectById(dto.getOpenCityId()); if (openCities == null) { throw new ServiceException("获取城市信息失败", I18nKeyConstant.CITY_NOT_FOUND); throw new ServiceException("城市暂未开通服务", I18nKeyConstant.CITY_NOT_FOUND); } XpOpenCity xpOpenCity = openCities; dto.setOpenCityId(xpOpenCity.getId()); xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages.properties
@@ -1,5 +1,5 @@ # 中文 city.not.found=获取城市信息失败 city.not.found=城市暂未开通服务 coupon.not.satisfy=券不满足条件 no.current.point=该站点已停止运营 no.current.region=当前区域未开通服务 xp-robotaxi-itinerary-boot/src/main/resources/i18n/messages_zh_CN.properties
@@ -1,5 +1,5 @@ # 中文 city.not.found=获取城市信息失败 city.not.found=城市暂未开通服务 coupon.not.satisfy=券不满足条件 no.current.point=该站点已停止运营 no.current.region=当前区域未开通服务 xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/schedule/ClockInScheduleTask.java
@@ -90,39 +90,30 @@ List<Long> fleetIdIn = clockOutPlans.stream().map(XpSchedulingDetail::getFleetId).collect(Collectors.toList()); List<Long> operationsUserIds = xpFleetOperations.stream().filter(x -> fleetIdIn.contains(x.getFleetId())) .map(XpFleetOperations::getOperationsUserId).distinct().collect(Collectors.toList()); // 批量查询人员打卡信息 List<XpClockIn> clockInList = xpClockInService.list(Wrappers.lambdaQuery(XpClockIn.class) .in(XpClockIn::getAppUserId, operationsUserIds).in(XpClockIn::getShiftId, ids)); // 判断有哪些人员没有打卡信息 List<Long> noClockInUserIds = operationsUserIds.stream().filter(id -> clockInList.stream().noneMatch(clockIn -> clockIn.getAppUserId().equals(id))).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(noClockInUserIds)){ log.info("========== 获取到{}个人员没有打卡信息 ==========", noClockInUserIds.size()); // 添加上班缺卡信息 List<XpClockIn> noClockInList = new ArrayList<>(); for (Long noClockInUserId : noClockInUserIds) { // 通过用户id找到车队id List<XpFleetOperations> operations = xpFleetOperations.stream().filter(x -> x.getOperationsUserId().equals(noClockInUserId)).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(operations)){ Long fleetId = operations.get(0).getFleetId(); // 通过车队id和已过期的班次获取排班明细 List<XpSchedulingDetail> clockOutPlanExpiredList = clockOutPlanList.stream() .filter(xpSchedulingDetail -> xpSchedulingDetail.getFleetId().equals(fleetId) && ids.contains(xpSchedulingDetail.getShiftId())).collect(Collectors.toList()); if(!CollectionUtils.isEmpty(clockOutPlanExpiredList)){ Long shiftId = clockOutPlanExpiredList.get(0).getShiftId(); XpClockIn xpClockIn = new XpClockIn(); xpClockIn.setAppUserId(noClockInUserId); xpClockIn.setClockInTime(LocalDateTime.now()); xpClockIn.setClockInResult(3); xpClockIn.setShiftId(shiftId); noClockInList.add(xpClockIn); } if(!CollectionUtils.isEmpty(operationsUserIds)){ int createdCount = 0; for (Long operationsUserId : operationsUserIds) { List<XpFleetOperations> operations = xpFleetOperations.stream() .filter(x -> x.getOperationsUserId().equals(operationsUserId)) .collect(Collectors.toList()); if(CollectionUtils.isEmpty(operations)){ continue; } Long fleetId = operations.get(0).getFleetId(); List<XpSchedulingDetail> clockOutPlanExpiredList = clockOutPlanList.stream() .filter(xpSchedulingDetail -> xpSchedulingDetail.getFleetId().equals(fleetId) && ids.contains(xpSchedulingDetail.getShiftId())) .collect(Collectors.toList()); if(CollectionUtils.isEmpty(clockOutPlanExpiredList)){ continue; } Long shiftId = clockOutPlanExpiredList.get(0).getShiftId(); boolean created = xpClockInService.createMissingClockInIfAbsent(operationsUserId, shiftId, LocalDate.now()); if (created) { createdCount++; } } if (!CollectionUtils.isEmpty(noClockInList)){ log.info("添加上班缺卡信息:{}", noClockInList); xpClockInService.saveBatch(noClockInList); } log.info("========== 新增上班缺卡记录{}条 ==========", createdCount); } } @@ -154,7 +145,6 @@ for (XpClockIn xpClockIn : clockInListFive) { xpClockIn.setClockOutTime(LocalDateTime.now()); xpClockIn.setClockOutResult(3); xpClockIn.setAppUserId(null); } xpClockInService.updateBatchById(clockInListFive); log.info("========== 修改为下班缺卡 =========="); @@ -198,7 +188,6 @@ log.info("========== 获取到{}个人员没有下班打卡信息 ==========", clockInListFive.size()); if(!CollectionUtils.isEmpty(clockInListFive)){ for (XpClockIn xpClockIn : clockInListFive) { xpClockIn.setAppUserId(null); xpClockIn.setClockOutTime(LocalDateTime.now()); xpClockIn.setClockOutResult(3); } xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/IXpClockInService.java
@@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.RequestBody; import javax.servlet.http.HttpServletResponse; import java.time.LocalDate; /** * <p> @@ -44,6 +45,17 @@ * @return */ R<String> clockOut(ClockDTO dto); /** * 补充上班缺卡记录 * * @param appUserId 用户ID * @param shiftId 班次ID * @param attendanceDate 考勤日期 * @return 是否新增成功 */ boolean createMissingClockInIfAbsent(Long appUserId, Long shiftId, LocalDate attendanceDate); /** * 检查用户是否上班中 * @param appUserId 用户ID xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
@@ -11,6 +11,7 @@ import com.xiaopeng.api.feignClient.account.XpOperationUserClient; import com.xiaopeng.api.feignClient.account.XpOperationUserNotificationClient; import com.xiaopeng.api.feignClient.pushMessage.XpAliPushMessageClient; import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient; 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; @@ -19,6 +20,7 @@ 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.model.vehicleConvoy.XpFleet; 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; @@ -37,6 +39,8 @@ import com.xiaopeng.maintenance.vo.ClockInStatisticsDetailsVo; import com.xiaopeng.maintenance.vo.ClockInStatisticsVo; import lombok.extern.slf4j.Slf4j; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -72,6 +76,10 @@ @Service public class XpClockInServiceImpl extends ServiceImpl<XpClockInMapper, XpClockIn> implements IXpClockInService { private static final String CLOCK_ATTENDANCE_LOCK_PREFIX = "CLOCK_ATTENDANCE_"; private static final long CLOCK_ATTENDANCE_LOCK_WAIT_SECONDS = 1L; private static final long CLOCK_ATTENDANCE_LOCK_LEASE_SECONDS = 10L; @Resource private XpCheckLocationMapper xpCheckLocationMapper; @Resource @@ -86,11 +94,16 @@ private IXpSchedulingDetailService xpSchedulingDetailService; @Resource private XpFleetClient xpFleetClient; @Resource private XpFleetOperationsClient fleetOperationsClient; @Resource private XpOperationUserNotificationClient xpOperationUserNotificationClient; @Resource private XpAliPushMessageClient xpAliPushMessageClient; @Resource private RedissonClient redissonClient; @@ -183,38 +196,132 @@ return R.fail("打卡失败"); } // 查询上班打卡信息 XpClockIn xpClockIn = xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class) .eq(XpClockIn::getAppUserId, dto.getOperationsUserId()) .likeRight(XpClockIn::getClockInTime, LocalDate.now()) LocalDate attendanceDate = LocalDate.now(); RLock lock = redissonClient.getLock(buildAttendanceLockKey(dto.getOperationsUserId(), attendanceDate)); try { boolean isLocked = lock.tryLock(CLOCK_ATTENDANCE_LOCK_WAIT_SECONDS, CLOCK_ATTENDANCE_LOCK_LEASE_SECONDS, TimeUnit.SECONDS); if (!isLocked) { return R.fail("系统繁忙,请稍后重试"); } LocalDateTime now = LocalDateTime.now(); XpClockIn xpClockIn = findTodayClockInRecord(dto.getOperationsUserId(), attendanceDate); boolean isNewRecord = false; if (Objects.isNull(xpClockIn)) { Long shiftId = resolveClockOutShiftId(dto.getOperationsUserId(), dto.getShiftId(), attendanceDate); if (Objects.isNull(shiftId)) { return R.fail("班次不存在"); } xpClockIn = buildMissingClockInRecord(dto.getOperationsUserId(), shiftId, now); isNewRecord = true; } XpShift xpShift = xpShiftMapper.selectById(xpClockIn.getShiftId()); if (null == xpShift) { return R.fail("班次不存在"); } fillClockOutInfo(xpClockIn, dto, xpShift, now, attendanceDate); if (isNewRecord) { this.save(xpClockIn); } else { this.updateById(xpClockIn); } sendAgreementUpdateNotification(now); return R.ok(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("下班打卡获取考勤锁被中断, appUserId={}", dto.getOperationsUserId(), e); return R.fail("系统繁忙,请稍后重试"); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } @Override public boolean createMissingClockInIfAbsent(Long appUserId, Long shiftId, LocalDate attendanceDate) { if (Objects.isNull(appUserId) || Objects.isNull(shiftId) || Objects.isNull(attendanceDate)) { return false; } RLock lock = redissonClient.getLock(buildAttendanceLockKey(appUserId, attendanceDate)); try { boolean isLocked = lock.tryLock(CLOCK_ATTENDANCE_LOCK_WAIT_SECONDS, CLOCK_ATTENDANCE_LOCK_LEASE_SECONDS, TimeUnit.SECONDS); if (!isLocked) { log.info("补上班缺卡获取锁失败, appUserId={}, attendanceDate={}", appUserId, attendanceDate); return false; } XpClockIn existingClockIn = findTodayClockInRecord(appUserId, attendanceDate); if (Objects.nonNull(existingClockIn)) { return false; } XpClockIn missingClockIn = buildMissingClockInRecord(appUserId, shiftId, LocalDateTime.now()); return this.save(missingClockIn); } catch (InterruptedException e) { Thread.currentThread().interrupt(); log.error("补上班缺卡获取考勤锁被中断, appUserId={}, attendanceDate={}", appUserId, attendanceDate, e); return false; } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } } private XpClockIn findTodayClockInRecord(Long appUserId, LocalDate attendanceDate) { LocalDateTime startTime = attendanceDate.atStartOfDay(); LocalDateTime endTime = attendanceDate.plusDays(1).atStartOfDay(); return xpClockInMapper.selectOne(Wrappers.lambdaQuery(XpClockIn.class) .eq(XpClockIn::getAppUserId, appUserId) .ge(XpClockIn::getClockInTime, startTime) .lt(XpClockIn::getClockInTime, endTime) .orderByDesc(XpClockIn::getClockInTime) .last("LIMIT 1")); if (Objects.isNull(xpClockIn)) { return R.fail("请先上班打卡"); } } // 查询班次 XpShift xpShift = xpShiftMapper.selectById(xpClockIn.getShiftId()); if (null == xpShift) { return R.fail("班次不存在"); private Long resolveClockOutShiftId(Long appUserId, Long fallbackShiftId, LocalDate attendanceDate) { R<XpFleet> fleetResult = xpFleetClient.getFleetByOperationsUserId(appUserId); XpFleet xpFleet = Objects.isNull(fleetResult) ? null : fleetResult.getData(); if (Objects.nonNull(xpFleet)) { XpSchedulingDetail schedulingDetail = xpSchedulingDetailService.getOne(Wrappers.lambdaQuery(XpSchedulingDetail.class) .eq(XpSchedulingDetail::getFleetId, xpFleet.getId()) .eq(XpSchedulingDetail::getPlanTime, attendanceDate) .last("LIMIT 1")); if (Objects.nonNull(schedulingDetail) && Objects.nonNull(schedulingDetail.getShiftId())) { return schedulingDetail.getShiftId(); } } // 开始打卡时间 String endTime = xpShift.getEndTime(); LocalDateTime endLocalDateTime = DateUtils.stringToLocalDateTime(LocalDate.now() + " " + endTime + ":00"); return fallbackShiftId; } LocalDateTime now = LocalDateTime.now(); private XpClockIn buildMissingClockInRecord(Long appUserId, Long shiftId, LocalDateTime now) { XpClockIn xpClockIn = new XpClockIn(); xpClockIn.setAppUserId(appUserId); xpClockIn.setClockInTime(now); xpClockIn.setClockInResult(3); xpClockIn.setShiftId(shiftId); return xpClockIn; } private void fillClockOutInfo(XpClockIn xpClockIn, ClockDTO dto, XpShift xpShift, LocalDateTime now, LocalDate attendanceDate) { LocalDateTime endLocalDateTime = DateUtils.stringToLocalDateTime(attendanceDate + " " + xpShift.getEndTime() + ":00"); xpClockIn.setClockOutTime(now); xpClockIn.setClockOutLon(dto.getUserLon()); xpClockIn.setClockOutLat(dto.getUserLat()); xpClockIn.setClockOutAddress(dto.getClockOutAddress()); if (xpClockIn.getClockOutTime().isBefore(endLocalDateTime)) { if (now.isBefore(endLocalDateTime)) { xpClockIn.setClockOutResult(2); } else { xpClockIn.setClockOutResult(1); } xpClockIn.setAppUserId(null); this.updateById(xpClockIn); sendAgreementUpdateNotification(now); return R.ok(); } private String buildAttendanceLockKey(Long appUserId, LocalDate attendanceDate) { return CLOCK_ATTENDANCE_LOCK_PREFIX + appUserId + "_" + attendanceDate; } @Override xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpTaskServiceImpl.java
@@ -233,24 +233,24 @@ if (Objects.isNull(vehicle)) { throw new ServiceException("上报失败,未获取到车辆信息", I18nKeyConstant.TASK_VEHICLE_NOT_EXIST); } VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicle.getVehicleVin())), VehiclePosition.class); if (Objects.isNull(vehiclePosition)) { log.error("事故上报未获取到车辆当前定位"); throw new ServiceException("获取车辆实时定位信息失败,请稍后重试", I18nKeyConstant.TASK_SAVE_ERROR); } xpTask.setVehicleLon(new BigDecimal(vehiclePosition.getPoint().getX())); xpTask.setVehicleLat(new BigDecimal(vehiclePosition.getPoint().getY())); Map<String, String> geocode = gdMapGeocodingUtil.geocode(xpTask.getVehicleLon().toString(), xpTask.getVehicleLat().toString()); String address = geocode == null ? null : geocode.get("address"); if (StringUtils.isBlank(address)) { log.error("事故上报通过车辆定位从高德获取地址信息失败"); throw new ServiceException("获取车辆实时定位信息失败,请稍后重试", I18nKeyConstant.TASK_SAVE_ERROR); } xpTask.setVehicleLocation(address); // VehiclePosition vehiclePosition = mongoTemplate.findOne(new Query(Criteria.where("vin").is(vehicle.getVehicleVin())), VehiclePosition.class); // if (Objects.isNull(vehiclePosition)) { // log.error("事故上报未获取到车辆当前定位"); // throw new ServiceException("获取车辆实时定位信息失败,请稍后重试", I18nKeyConstant.TASK_SAVE_ERROR); // } // xpTask.setVehicleLon(new BigDecimal(vehiclePosition.getPoint().getX())); // xpTask.setVehicleLat(new BigDecimal(vehiclePosition.getPoint().getY())); // Map<String, String> geocode = gdMapGeocodingUtil.geocode(xpTask.getVehicleLon().toString(), xpTask.getVehicleLat().toString()); // String address = geocode == null ? null : geocode.get("address"); // if (StringUtils.isBlank(address)) { // log.error("事故上报通过车辆定位从高德获取地址信息失败"); // throw new ServiceException("获取车辆实时定位信息失败,请稍后重试", I18nKeyConstant.TASK_SAVE_ERROR); // } // xpTask.setVehicleLocation(address); // 临时处理 // xpTask.setVehicleLon(new BigDecimal("104.055683")); // xpTask.setVehicleLat(new BigDecimal("30.585991")); // xpTask.setVehicleLocation("四川省成都市武侯区石羊街道天府新谷"); xpTask.setVehicleLon(new BigDecimal("104.055683")); xpTask.setVehicleLat(new BigDecimal("30.585991")); xpTask.setVehicleLocation("四川省成都市武侯区石羊街道天府新谷"); xpTask.setVehicleId(dto.getId()); xpTask.setReportUserId(xpOperationsUser.getId()); xpTask.setReportTime(LocalDateTime.now()); xp-robotaxi-map-boot/src/main/java/com/xiaopeng/map/service/serviceimpl/XpPointServiceImpl.java
@@ -303,7 +303,10 @@ List<XpPoint> list = this.baseMapper.listAll(query); for (XpPoint xpPoint : list) { String pointPictures = xpPoint.getPointPictures(); log.info("pointPictures:{}", pointPictures); if (StringUtils.isNotBlank(pointPictures)) { log.info("pointPictures进入处理:{}", pointPictures); JSONArray jsonArray = JSONArray.parseArray(pointPictures); for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); @@ -313,12 +316,15 @@ String downloadUrl =""; try { downloadUrl = aliOssService.getDownloadUrl(jsonObject.getString("id"), jsonObject.getString("name"), true); log.info("downloadUrl:{}", downloadUrl); } catch (UnsupportedEncodingException e) { log.error("获取图片下载地址失败", e); } jsonObject.put("url", downloadUrl); } log.info("jsonArray:{}", jsonArray); xpPoint.setPointPictures(JSONArray.toJSONString(jsonArray)); log.info("xpPoint:{}", xpPoint.getPointPictures()); } } xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/controller/XpVehicleController.java
@@ -770,7 +770,7 @@ @PostMapping("/inner/vehicle/queryVehiclePositionByVin") public R<List<String>> queryVehiclePositionByVin(@RequestBody VehiclePositionByVinDto dto){ List<XpVehicle> list = xpVehicleService.list(new LambdaQueryWrapper<XpVehicle>().eq(XpVehicle::getState, 0) .eq(XpVehicle::getOperatingStatus, 1).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin, .eq(XpVehicle::getOperatingStatus, 1).eq(XpVehicle::getVehicleType,2).eq(XpVehicle::getElectronicId, dto.getFenceId()).in(XpVehicle::getVehicleVin, dto.getVinList())); if(list.isEmpty()){ return R.ok(new ArrayList<>()); xp-robotaxi-vehicle-convoy-boot/src/main/java/com/xiaopeng/vehicleConvoy/service/impl/XpVehicleServiceImpl.java
@@ -415,7 +415,7 @@ String vin = xpVehicle.getVehicleVin(); R<String> xpuIdByVin = xpuVinClient.getXpuIdByVin(vin); if(200 != xpuIdByVin.getCode()){ throw new RuntimeException(xpuIdByVin.getMsg()); throw new ServiceException(xpuIdByVin.getMsg()); } String xpuid = xpuIdByVin.getData(); xpVehicle.setXpuId(xpuid); @@ -460,7 +460,7 @@ String vin = xpVehicle.getVehicleVin(); R<String> xpuIdByVin = xpuVinClient.getXpuIdByVin(vin); if(200 != xpuIdByVin.getCode()){ throw new RuntimeException(xpuIdByVin.getMsg()); throw new ServiceException(xpuIdByVin.getMsg()); } String xpuid = xpuIdByVin.getData(); xpVehicle.setXpuId(xpuid);