From a4ed0f250009167453ca637ce042ef8e0b69f613 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期六, 24 十月 2026 19:30:00 +0800
Subject: [PATCH] feat(aftersales): 增加售后退货流程管理功能
---
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java | 130 ++++++++++++++++++++++++++++++++++++-------
1 files changed, 108 insertions(+), 22 deletions(-)
diff --git a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
index 3780f24..8c38076 100644
--- a/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
+++ b/yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/attendance/HrmAttendanceRecordServiceImpl.java
@@ -6,6 +6,7 @@
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordPageReqVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRecordRespVO;
import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceRuleRespVO;
+import cn.iocoder.yudao.module.hrm.controller.admin.attendance.vo.HrmAttendanceClockReqVO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceHolidayDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRecordDO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.attendance.HrmAttendanceRuleDO;
@@ -33,6 +34,7 @@
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
+import cn.hutool.core.util.StrUtil;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*;
@@ -316,7 +318,8 @@
@Override
@Transactional(rollbackFor = Exception.class)
- public HrmAttendanceRecordDO clockIn(Long userId, LocalDateTime clockTime, String location) {
+ public HrmAttendanceClockResult clockIn(Long userId, LocalDateTime clockTime,
+ HrmAttendanceClockReqVO reqVO, String legacyLocation) {
LocalDate clockDate = clockTime.toLocalDate();
// 1. 鏍¢獙鏄惁宸ヤ綔鏃�
if (!attendanceHolidayService.isWorkDay(clockDate)) {
@@ -336,7 +339,9 @@
if (record != null && record.getClockInTime() != null) {
throw exception(ATTENDANCE_CLOCK_ALREADY_CLOCKED);
}
- // 4. 鍒ゆ柇鎵撳崱绫诲瀷
+ // 4. 鍦ㄤ换浣曞啓搴撴搷浣滃墠鏍¢獙浣嶇疆锛涗笉鑳界敤鏃х増 location 瀛楃涓茬粫杩囪寖鍥撮檺鍒躲��
+ ClockLocation clockLocation = validateClockLocation(rule, reqVO);
+ // 5. 鍒ゆ柇鎵撳崱绫诲瀷
LocalTime workStartTime = rule.getWorkStartTime();
int lateAllowMinutes = rule.getLateAllowMinutes() != null ? rule.getLateAllowMinutes() : 0;
LocalTime lateThreshold = workStartTime.plusMinutes(lateAllowMinutes);
@@ -344,7 +349,7 @@
Integer clockInType = clockTimeOnly.isBefore(lateThreshold) || clockTimeOnly.equals(lateThreshold)
? HrmAttendanceClockTypeEnum.NORMAL.getType()
: HrmAttendanceClockTypeEnum.LATE.getType();
- // 5. 鍒涘缓鎴栨洿鏂拌�冨嫟璁板綍
+ // 6. 鍒涘缓鎴栨洿鏂拌�冨嫟璁板綍
if (record == null) {
record = HrmAttendanceRecordDO.builder()
.userId(userId)
@@ -352,24 +357,24 @@
.date(clockDate)
.clockInTime(clockTime)
.clockInType(clockInType)
- .location(location)
.dataSource(HrmAttendanceDataSourceEnum.CLOCK.getType())
.build();
+ applyClockLocation(record, rule, clockLocation, legacyLocation, true);
attendanceRecordMapper.insert(record);
} else {
record.setClockInTime(clockTime);
record.setClockInType(clockInType);
- if (location != null) {
- record.setLocation(location);
- }
+ applyClockLocation(record, rule, clockLocation, legacyLocation, true);
attendanceRecordMapper.updateById(record);
}
- return record;
+ return new HrmAttendanceClockResult(record, rule,
+ clockLocation != null ? clockLocation.distanceMeters() : null);
}
@Override
@Transactional(rollbackFor = Exception.class)
- public HrmAttendanceRecordDO clockOut(Long userId, LocalDateTime clockTime, String location) {
+ public HrmAttendanceClockResult clockOut(Long userId, LocalDateTime clockTime,
+ HrmAttendanceClockReqVO reqVO, String legacyLocation) {
LocalDate clockDate = clockTime.toLocalDate();
// 1. 鏍¢獙鏄惁宸ヤ綔鏃�
if (!attendanceHolidayService.isWorkDay(clockDate)) {
@@ -386,8 +391,12 @@
}
// 3. 鏌ヨ褰撴棩鑰冨嫟璁板綍
HrmAttendanceRecordDO record = attendanceRecordMapper.selectByUserIdAndDate(userId, clockDate);
+ if (record != null && record.getClockOutTime() != null) {
+ throw exception(ATTENDANCE_CLOCK_ALREADY_CLOCKED);
+ }
+ ClockLocation clockLocation = validateClockLocation(rule, reqVO);
+ // 4. 鏃犱笂鐝崱鏃朵篃鍏堝畬鎴愪綅缃牎楠岋紝鍐嶅垱寤虹己鍗¤褰曘��
if (record == null) {
- // 濡傛灉娌℃湁涓婄彮鎵撳崱璁板綍锛屽垱寤轰竴鏉$己鍗$殑璁板綍
record = HrmAttendanceRecordDO.builder()
.userId(userId)
.deptId(employee.getDeptId())
@@ -395,12 +404,8 @@
.clockInType(HrmAttendanceClockTypeEnum.MISSING.getType())
.dataSource(HrmAttendanceDataSourceEnum.CLOCK.getType())
.build();
- attendanceRecordMapper.insert(record);
}
- if (record.getClockOutTime() != null) {
- throw exception(ATTENDANCE_CLOCK_ALREADY_CLOCKED);
- }
- // 4. 鍒ゆ柇鎵撳崱绫诲瀷
+ // 5. 鍒ゆ柇鎵撳崱绫诲瀷
LocalTime workEndTime = rule.getWorkEndTime();
int earlyAllowMinutes = rule.getEarlyAllowMinutes() != null ? rule.getEarlyAllowMinutes() : 0;
LocalTime earlyThreshold = workEndTime.minusMinutes(earlyAllowMinutes);
@@ -408,12 +413,10 @@
Integer clockOutType = clockTimeOnly.isAfter(earlyThreshold) || clockTimeOnly.equals(earlyThreshold)
? HrmAttendanceClockTypeEnum.NORMAL.getType()
: HrmAttendanceClockTypeEnum.EARLY.getType();
- // 5. 鏇存柊鑰冨嫟璁板綍
+ // 6. 鏇存柊鑰冨嫟璁板綍
record.setClockOutTime(clockTime);
record.setClockOutType(clockOutType);
- if (location != null) {
- record.setLocation(location);
- }
+ applyClockLocation(record, rule, clockLocation, legacyLocation, false);
// 6. 璁$畻宸ヤ綔鏃堕暱
if (record.getClockInTime() != null) {
Duration duration = Duration.between(record.getClockInTime(), clockTime);
@@ -423,8 +426,91 @@
}
// 7. 璁$畻鍔犵彮鏃堕暱锛氳秴杩囪鍒欎笅鐝椂闂寸殑閮ㄥ垎
record.setOvertimeHours(computeOvertimeHours(rule.getWorkEndTime(), clockTimeOnly));
- attendanceRecordMapper.updateById(record);
- return record;
+ if (record.getId() == null) {
+ attendanceRecordMapper.insert(record);
+ } else {
+ attendanceRecordMapper.updateById(record);
+ }
+ return new HrmAttendanceClockResult(record, rule,
+ clockLocation != null ? clockLocation.distanceMeters() : null);
+ }
+
+ record ClockLocation(BigDecimal longitude, BigDecimal latitude, BigDecimal accuracy,
+ BigDecimal distanceMeters) { }
+
+ ClockLocation validateClockLocation(HrmAttendanceRuleDO rule, HrmAttendanceClockReqVO reqVO) {
+ boolean required = Boolean.TRUE.equals(rule.getLocationRequired());
+ if (required && (StrUtil.isBlank(rule.getLocationName()) || rule.getLongitude() == null
+ || rule.getLatitude() == null || rule.getRadiusMeters() == null
+ || rule.getRadiusMeters() < 50 || rule.getRadiusMeters() > 5000
+ || !"WGS84".equalsIgnoreCase(rule.getCoordinateSystem()))) {
+ throw exception(ATTENDANCE_LOCATION_RULE_INVALID);
+ }
+ if (reqVO == null || reqVO.getLongitude() == null || reqVO.getLatitude() == null
+ || reqVO.getAccuracy() == null || StrUtil.isBlank(reqVO.getCoordinateSystem())) {
+ if (required) throw exception(ATTENDANCE_LOCATION_REQUIRED);
+ return null;
+ }
+ boolean coordinatesValid = reqVO.getLongitude().compareTo(BigDecimal.valueOf(-180)) >= 0
+ && reqVO.getLongitude().compareTo(BigDecimal.valueOf(180)) <= 0
+ && reqVO.getLatitude().compareTo(BigDecimal.valueOf(-90)) >= 0
+ && reqVO.getLatitude().compareTo(BigDecimal.valueOf(90)) <= 0;
+ if (!coordinatesValid) {
+ if (required) throw exception(ATTENDANCE_LOCATION_COORDINATE_INVALID);
+ return null;
+ }
+ if (reqVO.getAccuracy().signum() <= 0 || reqVO.getAccuracy().compareTo(BigDecimal.valueOf(100)) > 0) {
+ if (required) throw exception(ATTENDANCE_LOCATION_ACCURACY_INVALID);
+ return null;
+ }
+ if (!"WGS84".equalsIgnoreCase(reqVO.getCoordinateSystem().trim())) {
+ if (required) throw exception(ATTENDANCE_LOCATION_SYSTEM_INVALID);
+ return null;
+ }
+ if (!required) {
+ return new ClockLocation(reqVO.getLongitude(), reqVO.getLatitude(), reqVO.getAccuracy(), null);
+ }
+ double rawDistance = haversineMeters(rule.getLongitude().doubleValue(), rule.getLatitude().doubleValue(),
+ reqVO.getLongitude().doubleValue(), reqVO.getLatitude().doubleValue());
+ BigDecimal roundedDistance = BigDecimal.valueOf(rawDistance).setScale(2, RoundingMode.HALF_UP);
+ if (rawDistance > rule.getRadiusMeters()) {
+ throw exception(ATTENDANCE_LOCATION_OUT_OF_RANGE, rule.getLocationName(),
+ roundedDistance.toPlainString(), rule.getRadiusMeters());
+ }
+ return new ClockLocation(reqVO.getLongitude(), reqVO.getLatitude(), reqVO.getAccuracy(), roundedDistance);
+ }
+
+ static double haversineMeters(double lon1, double lat1, double lon2, double lat2) {
+ double latDelta = Math.toRadians(lat2 - lat1);
+ double lonDelta = Math.toRadians(lon2 - lon1);
+ double a = Math.pow(Math.sin(latDelta / 2), 2)
+ + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
+ * Math.pow(Math.sin(lonDelta / 2), 2);
+ double bounded = Math.max(0D, Math.min(1D, a));
+ return 6_371_000D * 2D * Math.atan2(Math.sqrt(bounded), Math.sqrt(1D - bounded));
+ }
+
+ private void applyClockLocation(HrmAttendanceRecordDO record, HrmAttendanceRuleDO rule,
+ ClockLocation location, String legacyLocation, boolean clockIn) {
+ if (StrUtil.isNotBlank(rule.getLocationName())) {
+ record.setLocation(rule.getLocationName());
+ } else if (!Boolean.TRUE.equals(rule.getLocationRequired()) && StrUtil.isNotBlank(legacyLocation)) {
+ record.setLocation(legacyLocation);
+ }
+ if (location == null) return;
+ if (clockIn) {
+ record.setClockInLongitude(location.longitude());
+ record.setClockInLatitude(location.latitude());
+ record.setClockInAccuracy(location.accuracy());
+ record.setClockInDistance(location.distanceMeters());
+ record.setClockInAddress(rule.getLocationAddress());
+ } else {
+ record.setClockOutLongitude(location.longitude());
+ record.setClockOutLatitude(location.latitude());
+ record.setClockOutAccuracy(location.accuracy());
+ record.setClockOutDistance(location.distanceMeters());
+ record.setClockOutAddress(rule.getLocationAddress());
+ }
}
/**
@@ -531,4 +617,4 @@
attendanceRecordMapper.updateById(record);
}
-}
\ No newline at end of file
+}
--
Gitblit v1.9.3