From e4282457d992fd59acdaf0de9f150105a7015b9c Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期二, 21 四月 2026 14:38:46 +0800
Subject: [PATCH] Merge branch 'master' of http://120.76.84.145:10101/gitblit/r/java/Robotaxi into 1.0
---
xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java | 143 ++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 127 insertions(+), 16 deletions(-)
diff --git a/xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java b/xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
index 3f5645f..3c95635 100644
--- a/xp-robotaxi-maintenance-boot/src/main/java/com/xiaopeng/maintenance/service/impl/XpClockInServiceImpl.java
+++ b/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,30 +196,124 @@
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);
@@ -217,6 +324,10 @@
return R.ok(xpClockIn.getClockOutResult());
}
+ private String buildAttendanceLockKey(Long appUserId, LocalDate attendanceDate) {
+ return CLOCK_ATTENDANCE_LOCK_PREFIX + appUserId + "_" + attendanceDate;
+ }
+
@Override
public Integer checkWorkStatus(Long appUserId) {
if (appUserId == null) {
--
Gitblit v1.7.1