chenshudong 4 місяців тому
батько
коміт
62a526c5ea
15 змінених файлів з 358 додано та 67 видалено
  1. 30 8
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendancePostRecordController.java
  2. 9 1
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceTeamUserRecordController.java
  3. 10 0
      airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/api/AttendanceController.java
  4. 12 0
      airport-attendance/src/main/java/com/sundot/airport/attendance/domain/AttendanceTeamUserRecord.java
  5. 8 0
      airport-attendance/src/main/java/com/sundot/airport/attendance/mapper/AttendancePostRecordMapper.java
  6. 16 0
      airport-attendance/src/main/java/com/sundot/airport/attendance/mapper/AttendanceTeamUserRecordMapper.java
  7. 8 0
      airport-attendance/src/main/java/com/sundot/airport/attendance/service/IAttendancePostRecordService.java
  8. 5 0
      airport-attendance/src/main/java/com/sundot/airport/attendance/service/IAttendanceRecordService.java
  9. 7 1
      airport-attendance/src/main/java/com/sundot/airport/attendance/service/IAttendanceTeamUserRecordService.java
  10. 10 1
      airport-attendance/src/main/java/com/sundot/airport/attendance/service/impl/AttendancePostRecordServiceImpl.java
  11. 127 15
      airport-attendance/src/main/java/com/sundot/airport/attendance/service/impl/AttendanceRecordServiceImpl.java
  12. 66 32
      airport-attendance/src/main/java/com/sundot/airport/attendance/service/impl/AttendanceTeamUserRecordServiceImpl.java
  13. 12 2
      airport-attendance/src/main/resources/mapper/attendance/AttendancePostRecordMapper.xml
  14. 1 1
      airport-attendance/src/main/resources/mapper/attendance/AttendanceRecordMapper.xml
  15. 37 6
      airport-attendance/src/main/resources/mapper/attendance/AttendanceTeamUserRecordMapper.xml

+ 30 - 8
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendancePostRecordController.java

@@ -124,8 +124,11 @@ public class AttendancePostRecordController extends BaseController {
124 124
     public AjaxResult listGroupByTimeAndUserId() {
125 125
         Long currentUserId = SecurityUtils.getUserId();
126 126
         // 计算当前时间对应的班次日期
127
-        Date queryDate = determineShiftDate();
128
-
127
+//        Date queryDate = determineShiftDate();
128
+        Date queryDate = attendancePostRecordService.selectMaxDateByUserId(currentUserId);
129
+        if (queryDate == null) {
130
+            queryDate = DateUtils.getNowDate();
131
+        }
129 132
         // 先获取当前用户今天的记录,取出班次信息
130 133
         List<AttendancePostRecord> userRecords = attendancePostRecordService.selectTodayRecordsByUserId(currentUserId, queryDate);
131 134
 
@@ -365,7 +368,8 @@ public class AttendancePostRecordController extends BaseController {
365 368
             record.setRevision(1L);
366 369
             record.setWorkDuration((record.getCheckOutTime().getTime() - record.getCheckInTime().getTime()) / 1000 / 60);
367 370
             // 使用班次日期逻辑设置考勤日期
368
-            Date shiftDate = determineShiftDate();
371
+//            Date shiftDate = determineShiftDate();
372
+            Date shiftDate = DateUtils.getNowDate();
369 373
             record.setAttendanceDate(shiftDate);
370 374
             record.setOverDuration(0L);
371 375
             record.setCreateBy(getUserId().toString());
@@ -395,8 +399,8 @@ public class AttendancePostRecordController extends BaseController {
395 399
             postRecord.setRevision(postRecord.getRevision() + 1);
396 400
             postRecord.setWorkDuration((postRecord.getCheckOutTime().getTime() - postRecord.getCheckInTime().getTime()) / 1000 / 60);
397 401
             // 使用班次日期逻辑设置考勤日期
398
-            Date shiftDate = determineShiftDate();
399
-            postRecord.setAttendanceDate(shiftDate);
402
+//            Date shiftDate = determineShiftDate();
403
+//            postRecord.setAttendanceDate(shiftDate);
400 404
             extracted(postRecord);
401 405
             // 判断是否已签退
402 406
             LocalDateTime baseDateTime = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
@@ -404,6 +408,18 @@ public class AttendancePostRecordController extends BaseController {
404 408
                 postRecord.setLocked(false);
405 409
             }
406 410
             count += attendancePostRecordService.updateAttendancePostRecord(postRecord);
411
+
412
+            // 处理考勤班组成员表
413
+            AttendanceTeamUserRecord queryAttendanceTeamUserRecord = new AttendanceTeamUserRecord();
414
+            queryAttendanceTeamUserRecord.setCreateBy(SecurityUtils.getUsername());
415
+            queryAttendanceTeamUserRecord.setUserId(postRecord.getUserId());
416
+            queryAttendanceTeamUserRecord.setCheckInType("1");
417
+            List<AttendanceTeamUserRecord> attendanceTeamUserRecordList = attendanceTeamUserRecordService.selectAttendanceTeamUserRecordList(queryAttendanceTeamUserRecord);
418
+            attendanceTeamUserRecordList.forEach(attendanceTeamUserRecord -> {
419
+                attendanceTeamUserRecord.setUpdateBy(SecurityUtils.getUsername());
420
+                attendanceTeamUserRecord.setCheckInType("2");
421
+                attendanceTeamUserRecordService.updateAttendanceTeamUserRecord(attendanceTeamUserRecord);
422
+            });
407 423
         }
408 424
 
409 425
 
@@ -1078,13 +1094,19 @@ public class AttendancePostRecordController extends BaseController {
1078 1094
     private boolean isUserRecordLocked(Long userId) {
1079 1095
         try {
1080 1096
             // 获取当前班次日期
1081
-            Date shiftDate = determineShiftDate();
1097
+//            Date shiftDate = determineShiftDate();
1098
+            Date shiftDate = attendancePostRecordService.selectMaxDateByUserId(userId);
1099
+            if (shiftDate == null) {
1100
+                shiftDate = DateUtils.getNowDate();
1101
+            }
1082 1102
             List<AttendancePostRecord> userRecords = attendancePostRecordService.selectTodayRecordsByUserId(userId, shiftDate);
1083 1103
 
1084 1104
             // 检查是否有已锁定且在当前班次周期内的记录
1105
+//            return userRecords.stream()
1106
+//                    .anyMatch(record -> record.getLocked() != null && record.getLocked()
1107
+//                            && isWithinCurrentShiftPeriod(record.getLockTime()));
1085 1108
             return userRecords.stream()
1086
-                    .anyMatch(record -> record.getLocked() != null && record.getLocked()
1087
-                            && isWithinCurrentShiftPeriod(record.getLockTime()));
1109
+                    .anyMatch(record -> record.getLocked() != null && record.getLocked());
1088 1110
         } catch (Exception e) {
1089 1111
             return false;
1090 1112
         }

+ 9 - 1
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/AttendanceTeamUserRecordController.java

@@ -55,7 +55,6 @@ public class AttendanceTeamUserRecordController extends BaseController {
55 55
     @PreAuthorize("@ss.hasPermi('attendance:record:list')")
56 56
     @GetMapping("/list")
57 57
     public TableDataInfo list(AttendanceTeamUserRecord attendanceTeamUserRecord) {
58
-        startPage();
59 58
         List<AttendanceTeamUserRecord> list = attendanceTeamUserRecordService.selectAttendanceTeamUserRecordList(attendanceTeamUserRecord);
60 59
         return getDataTable(list);
61 60
     }
@@ -132,4 +131,13 @@ public class AttendanceTeamUserRecordController extends BaseController {
132 131
         return success(attendanceTeamUserRecordService.selectAttendanceTeamLeaderId());
133 132
     }
134 133
 
134
+    /**
135
+     * 根据主键删除考勤班组成员
136
+     */
137
+    @PreAuthorize("@ss.hasPermi('attendance:record:remove')")
138
+    @Log(title = "考勤班组成员", businessType = BusinessType.DELETE)
139
+    @DeleteMapping("/removeBy/{ids}")
140
+    public AjaxResult removeBy(@PathVariable Long[] ids) {
141
+        return toAjax(attendanceTeamUserRecordService.deleteAttendanceTeamUserRecordByIds(ids));
142
+    }
135 143
 }

+ 10 - 0
airport-admin/src/main/java/com/sundot/airport/web/controller/attendance/api/AttendanceController.java

@@ -16,6 +16,7 @@ import com.sundot.airport.system.service.ISysConfigService;
16 16
 import com.sundot.airport.web.core.cache.UserCache;
17 17
 import org.springframework.beans.factory.annotation.Autowired;
18 18
 import org.springframework.transaction.annotation.Transactional;
19
+import org.springframework.web.bind.annotation.GetMapping;
19 20
 import org.springframework.web.bind.annotation.PostMapping;
20 21
 import org.springframework.web.bind.annotation.RequestBody;
21 22
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -60,4 +61,13 @@ public class AttendanceController extends BaseController {
60 61
     public AjaxResult recordList(@RequestBody AttendanceRecordReq dto) {
61 62
         return success(attendanceRecordService.recordList(dto));
62 63
     }
64
+
65
+    /**
66
+     * 查询可打卡类型:1=签到,2=签退
67
+     */
68
+    @GetMapping("/v1/getRecordType")
69
+    public AjaxResult getRecordType() {
70
+        return AjaxResult.success("操作成功", attendanceRecordService.getRecordType());
71
+    }
72
+
63 73
 }

+ 12 - 0
airport-attendance/src/main/java/com/sundot/airport/attendance/domain/AttendanceTeamUserRecord.java

@@ -23,6 +23,11 @@ public class AttendanceTeamUserRecord extends BaseEntity {
23 23
     private static final long serialVersionUID = 1L;
24 24
 
25 25
     /**
26
+     * 主键
27
+     */
28
+    private Long id;
29
+
30
+    /**
26 31
      * 用户ID
27 32
      */
28 33
     @Excel(name = "用户ID")
@@ -79,6 +84,13 @@ public class AttendanceTeamUserRecord extends BaseEntity {
79 84
     @ApiModelProperty("考勤班组名称")
80 85
     private String attendanceTeamName;
81 86
 
87
+    /**
88
+     * 类型:1-已上通道,2-已下通道
89
+     */
90
+    @Excel(name = "类型:1-已上通道,2-已下通道")
91
+    @ApiModelProperty("类型:1-已上通道,2-已下通道")
92
+    private String checkInType;
93
+
82 94
     private Date attendanceDateStart;
83 95
 
84 96
     private Date attendanceDateEnd;

+ 8 - 0
airport-attendance/src/main/java/com/sundot/airport/attendance/mapper/AttendancePostRecordMapper.java

@@ -136,4 +136,12 @@ public interface AttendancePostRecordMapper {
136 136
      * @return
137 137
      */
138 138
     List<AttendancePostRecord> selectTeamOnList(@Param("date") Date date, @Param("attendanceTeamId") Long attendanceTeamId);
139
+
140
+    /**
141
+     * 查询指定用户最近的上岗记录的考勤日期
142
+     *
143
+     * @param userId 用户ID
144
+     * @return 考勤日期
145
+     */
146
+    public Date selectMaxDateByUserId(@Param("userId") Long userId);
139 147
 }

+ 16 - 0
airport-attendance/src/main/java/com/sundot/airport/attendance/mapper/AttendanceTeamUserRecordMapper.java

@@ -72,4 +72,20 @@ public interface AttendanceTeamUserRecordMapper {
72 72
      * @return 考勤班组成员列表
73 73
      */
74 74
     List<AttendanceTeamUserRecord> selectAttendanceTeamUserRecordByUserIdAndDateRange(Long userId, Date startDate, Date endDate);
75
+
76
+    /**
77
+     * 批量删除考勤班组成员
78
+     *
79
+     * @param ids 需要删除的数据主键集合
80
+     * @return 结果
81
+     */
82
+    public int deleteAttendanceTeamUserRecordByIds(Long[] ids);
83
+
84
+    /**
85
+     * 查询指定用户最近维护班组长成员时的考勤日期
86
+     *
87
+     * @param attendanceTeamUserRecord 查询条件
88
+     * @return 考勤日期
89
+     */
90
+    public Date selectMaxDateBy(AttendanceTeamUserRecord attendanceTeamUserRecord);
75 91
 }

+ 8 - 0
airport-attendance/src/main/java/com/sundot/airport/attendance/service/IAttendancePostRecordService.java

@@ -139,4 +139,12 @@ public interface IAttendancePostRecordService {
139 139
      * @return 时间
140 140
      */
141 141
     Map<String, Date> getShiftTime();
142
+
143
+    /**
144
+     * 查询指定用户最近的上岗记录的考勤日期
145
+     *
146
+     * @param userId 用户ID
147
+     * @return 考勤日期
148
+     */
149
+    public Date selectMaxDateByUserId(Long userId);
142 150
 }

+ 5 - 0
airport-attendance/src/main/java/com/sundot/airport/attendance/service/IAttendanceRecordService.java

@@ -92,4 +92,9 @@ public interface IAttendanceRecordService {
92 92
      * @return 计算出的考勤日期,表示该次打卡记录应归属的考勤周期日期
93 93
      */
94 94
     Date calculateAttendanceDate(Date checkInTime, String shiftStartTime, String shiftEndTime, String checkInType);
95
+
96
+    /**
97
+     * 查询可打卡类型:1=签到,2=签退
98
+     */
99
+    String getRecordType();
95 100
 }

+ 7 - 1
airport-attendance/src/main/java/com/sundot/airport/attendance/service/IAttendanceTeamUserRecordService.java

@@ -82,5 +82,11 @@ public interface IAttendanceTeamUserRecordService {
82 82
      */
83 83
     List<Long> selectAttendanceTeamLeaderId();
84 84
 
85
-
85
+    /**
86
+     * 批量删除考勤班组成员
87
+     *
88
+     * @param ids 需要删除的考勤班组成员主键集合
89
+     * @return 结果
90
+     */
91
+    public int deleteAttendanceTeamUserRecordByIds(Long[] ids);
86 92
 }

+ 10 - 1
airport-attendance/src/main/java/com/sundot/airport/attendance/service/impl/AttendancePostRecordServiceImpl.java

@@ -267,5 +267,14 @@ public class AttendancePostRecordServiceImpl implements IAttendancePostRecordSer
267 267
         }
268 268
     }
269 269
 
270
-
270
+    /**
271
+     * 查询指定用户最近的上岗记录的考勤日期
272
+     *
273
+     * @param userId 用户ID
274
+     * @return 考勤日期
275
+     */
276
+    @Override
277
+    public Date selectMaxDateByUserId(Long userId) {
278
+        return attendancePostRecordMapper.selectMaxDateByUserId(userId);
279
+    }
271 280
 }

+ 127 - 15
airport-attendance/src/main/java/com/sundot/airport/attendance/service/impl/AttendanceRecordServiceImpl.java

@@ -5,24 +5,32 @@ import cn.hutool.core.collection.CollectionUtil;
5 5
 import cn.hutool.core.util.ObjectUtil;
6 6
 import cn.hutool.core.util.StrUtil;
7 7
 import com.sundot.airport.attendance.domain.AttendanceCheckRecord;
8
+import com.sundot.airport.attendance.domain.AttendancePostRecord;
8 9
 import com.sundot.airport.attendance.domain.AttendanceRecord;
10
+import com.sundot.airport.attendance.domain.AttendanceTeamUserRecord;
9 11
 import com.sundot.airport.attendance.enums.CheckInTypeEnum;
10 12
 import com.sundot.airport.attendance.mapper.AttendanceCheckRecordMapper;
11 13
 import com.sundot.airport.attendance.mapper.AttendanceRecordMapper;
14
+import com.sundot.airport.attendance.service.IAttendancePostRecordService;
12 15
 import com.sundot.airport.attendance.service.IAttendanceRecordService;
16
+import com.sundot.airport.attendance.service.IAttendanceTeamUserRecordService;
17
+import com.sundot.airport.common.core.domain.entity.SysRole;
13 18
 import com.sundot.airport.common.dto.AttendanceRecordDTO;
14 19
 import com.sundot.airport.common.dto.AttendanceRecordRep;
15 20
 import com.sundot.airport.common.dto.AttendanceRecordReq;
16 21
 import com.sundot.airport.common.dto.UserInfo;
22
+import com.sundot.airport.common.enums.RoleTypeEnum;
17 23
 import com.sundot.airport.common.exception.ServiceException;
18 24
 import com.sundot.airport.common.utils.DateUtils;
19 25
 import com.sundot.airport.common.utils.SecurityUtils;
20 26
 import com.sundot.airport.exam.event.ClockInEvent;
21 27
 import com.sundot.airport.system.service.ISysConfigService;
28
+import lombok.extern.slf4j.Slf4j;
22 29
 import org.apache.commons.compress.utils.Lists;
23 30
 import org.springframework.beans.factory.annotation.Autowired;
24 31
 import org.springframework.context.ApplicationEventPublisher;
25 32
 import org.springframework.stereotype.Service;
33
+import org.springframework.transaction.annotation.Transactional;
26 34
 
27 35
 import java.time.LocalDate;
28 36
 import java.time.LocalDateTime;
@@ -39,9 +47,14 @@ import java.util.stream.Collectors;
39 47
  * @author wangchong
40 48
  * @date 2025-07-10
41 49
  */
50
+@Slf4j
42 51
 @Service
43 52
 public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
44 53
     @Autowired
54
+    private IAttendancePostRecordService attendancePostRecordService;
55
+    @Autowired
56
+    private IAttendanceTeamUserRecordService attendanceTeamUserRecordService;
57
+    @Autowired
45 58
     private AttendanceRecordMapper attendanceRecordMapper;
46 59
 
47 60
     @Autowired
@@ -132,21 +145,23 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
132 145
      * @param userInfo 登录用户
133 146
      * @return 今日打卡历史
134 147
      */
148
+    @Transactional(rollbackFor = Exception.class)
135 149
     @Override
136 150
     public List<AttendanceCheckRecord> record(AttendanceRecordDTO dto, UserInfo userInfo) {
151
+        // 考勤记录数据检查
152
+        checkData(dto, userInfo);
137 153
         //确定打卡时间
138
-        if (ObjectUtil.isNotEmpty(dto.getCheckInDate())) {
139
-            String startTime = configService.selectConfigByKey("attendance.record.start.time");
140
-            String endTime = configService.selectConfigByKey("attendance.record.end.time");
141
-            Date checkInTime = this.calculateAttendanceDate(new Date(), startTime, endTime, dto.getCheckInType());
142
-            dto.setCheckInDate(checkInTime);
143
-        }
154
+//        if (ObjectUtil.isNotEmpty(dto.getCheckInDate())) {
155
+//            String startTime = configService.selectConfigByKey("attendance.record.start.time");
156
+//            String endTime = configService.selectConfigByKey("attendance.record.end.time");
157
+//            Date checkInTime = this.calculateAttendanceDate(new Date(), startTime, endTime, dto.getCheckInType());
158
+//            dto.setCheckInDate(checkInTime);
159
+//        }
144 160
         //如果下班卡,直接取最近一条上班打卡记录
145 161
         if (StrUtil.equals("2", dto.getCheckInType())) {
146 162
             AttendanceRecordReq dtoReq = new AttendanceRecordReq();
147 163
             dtoReq.setUserId(userInfo.getUserId());
148
-            dtoReq.setCheckInDate(dto.getCheckInDate());
149
-            List<AttendanceRecord> attendanceRecords = queryRecordList(dtoReq);
164
+            List<AttendanceRecord> attendanceRecords = queryRecordListNew(dtoReq);
150 165
             if (CollectionUtil.isNotEmpty(attendanceRecords)) {
151 166
                 AttendanceRecord lastRecord = attendanceRecords.get(0);
152 167
                 dto.setCheckInDate(lastRecord.getAttendanceDate());
@@ -168,7 +183,7 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
168 183
         checkRecord.setCreateTime(new Date());
169 184
 
170 185
         // 处理梭班
171
-        processShuttleShiftLogic(checkRecord, userInfo);
186
+//        processShuttleShiftLogic(checkRecord, userInfo);
172 187
         // 保存打卡记录
173 188
         attendanceCheckRecordMapper.insertAttendanceCheckRecord(checkRecord);
174 189
 
@@ -188,19 +203,64 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
188 203
         }
189 204
 
190 205
         // 处理考勤记录
191
-        AttendanceRecord search = new AttendanceRecord();
192
-        search.setUserId(userInfo.getUserId());
193
-        search.setAttendanceDate(dto.getCheckInDate());
194
-        List<AttendanceRecord> attendanceCheckRecords = attendanceRecordMapper.selectAttendanceRecordList(search);
195
-        if (CollectionUtil.isEmpty(attendanceCheckRecords)) {
206
+        if (StrUtil.equals("1", dto.getCheckInType())) {
196 207
             AttendanceRecord attendanceRecord = getAttendanceRecord(dto, userInfo);
197 208
             attendanceRecordMapper.insertAttendanceRecord(attendanceRecord);
198 209
         } else {
210
+            AttendanceRecord search = new AttendanceRecord();
211
+            search.setUserId(userInfo.getUserId());
212
+            search.setAttendanceDate(dto.getCheckInDate());
213
+            List<AttendanceRecord> attendanceCheckRecords = attendanceRecordMapper.selectAttendanceRecordList(search);
199 214
             AttendanceRecord attendanceRecord = getAttendanceRecord(userInfo, attendanceCheckRecords);
200 215
             attendanceRecord.setRemark(attendanceRecord.getRemark() + "\n" + dto.getRemark());
201 216
             attendanceRecordMapper.updateAttendanceRecord(attendanceRecord);
202 217
         }
203 218
 
219
+        // 签退处理上岗记录表+考勤班组成员表
220
+        if (StrUtil.equals("2", dto.getCheckInType())) {
221
+            List<SysRole> roles = SecurityUtils.getLoginUser().getUser().getRoles();
222
+            if (CollectionUtil.isNotEmpty(roles)) {
223
+                List<String> roleKeyList = roles.stream().map(SysRole::getRoleKey).collect(Collectors.toList());
224
+                if (roleKeyList.contains(RoleTypeEnum.banzuzhang.getCode()) || roleKeyList.contains(RoleTypeEnum.kezhang.getCode())) {
225
+                    // 处理上岗记录表
226
+                    // 查询在该时间段内未签退的记录
227
+                    AttendancePostRecord queryParam = new AttendancePostRecord();
228
+                    queryParam.setCreateBy(SecurityUtils.getUserId().toString());
229
+                    queryParam.setCheckOutTimeType(1); // 未签退的记录
230
+                    List<AttendancePostRecord> records = attendancePostRecordService.selectAttendancePostRecordList(queryParam);
231
+                    // 自动签退这些记录
232
+                    Date autoCheckOutTime = DateUtils.getNowDate();
233
+                    int count = 0;
234
+                    for (AttendancePostRecord record : records) {
235
+                        try {
236
+                            record.setCheckOutTime(autoCheckOutTime);
237
+                            record.setWorkDuration((autoCheckOutTime.getTime() - record.getCheckInTime().getTime()) / 1000 / 60);
238
+                            record.setUpdateBy(SecurityUtils.getUserId().toString());
239
+                            record.setUpdateTime(new Date());
240
+                            record.setRemark((record.getRemark() != null ? record.getRemark() : "") + " 系统自动签退");
241
+                            record.setLocked(false);
242
+                            attendancePostRecordService.updateAttendancePostRecord(record);
243
+                            count++;
244
+                        } catch (Exception e) {
245
+                            log.error("自动签退记录失败,ID: " + record.getId(), e);
246
+                        }
247
+                    }
248
+                    log.info("自动下通道任务执行完成,共处理 {} 条记录", count);
249
+
250
+                    // 处理考勤班组成员表
251
+                    AttendanceTeamUserRecord queryAttendanceTeamUserRecord = new AttendanceTeamUserRecord();
252
+                    queryAttendanceTeamUserRecord.setCreateBy(SecurityUtils.getUsername());
253
+                    queryAttendanceTeamUserRecord.setCheckInType("1");
254
+                    List<AttendanceTeamUserRecord> attendanceTeamUserRecordList = attendanceTeamUserRecordService.selectAttendanceTeamUserRecordList(queryAttendanceTeamUserRecord);
255
+                    attendanceTeamUserRecordList.forEach(attendanceTeamUserRecord -> {
256
+                        attendanceTeamUserRecord.setUpdateBy(SecurityUtils.getUsername());
257
+                        attendanceTeamUserRecord.setCheckInType("2");
258
+                        attendanceTeamUserRecordService.updateAttendanceTeamUserRecord(attendanceTeamUserRecord);
259
+                    });
260
+                }
261
+            }
262
+        }
263
+
204 264
         AttendanceCheckRecord checkSearch = new AttendanceCheckRecord();
205 265
         checkSearch.setUserId(userInfo.getUserId());
206 266
         checkRecord.setCheckInDate(dto.getCheckInDate());
@@ -220,6 +280,10 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
220 280
             attendanceRecord.setDepartmentCode(userInfo.getDepartmentId().toString());
221 281
         }
222 282
         attendanceRecord.setDepartmentName(userInfo.getDepartmentName());
283
+        if (Objects.nonNull(userInfo.getBrigadeId())) {
284
+            attendanceRecord.setBrigadeCode(userInfo.getBrigadeId().toString());
285
+        }
286
+        attendanceRecord.setBrigadeName(userInfo.getBrigadeName());
223 287
         if (Objects.nonNull(userInfo.getStationId())) {
224 288
             attendanceRecord.setStationCode(userInfo.getStationId().toString());
225 289
         }
@@ -262,7 +326,7 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
262 326
             // 计算查询时间范围
263 327
             Date yesterday = DateUtils.addDays(targetAttendanceDate, -1);
264 328
             // 设置查询条件
265
-            recordSearch.setAttendanceDateStart(yesterday);
329
+            recordSearch.setAttendanceDateStart(targetAttendanceDate);
266 330
             recordSearch.setAttendanceDateEnd(targetAttendanceDate);
267 331
         } else {
268 332
             recordSearch.setAttendanceDateStart(dto.getStartDate());
@@ -308,6 +372,7 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
308 372
      * @param checkInType    打卡类型:1-上班打卡,2-下班打卡
309 373
      * @return 计算出的考勤日期,该次打卡记录应归属的考勤周期日期
310 374
      */
375
+    @Override
311 376
     public Date calculateAttendanceDate(Date checkInTime, String shiftStartTime, String shiftEndTime, String checkInType) {
312 377
         // 参数校验
313 378
         if (checkInTime == null || shiftStartTime == null || shiftEndTime == null) {
@@ -476,5 +541,52 @@ public class AttendanceRecordServiceImpl implements IAttendanceRecordService {
476 541
         return attendanceRecordMapper.selectAttendanceRecordList(recordSearch);
477 542
     }
478 543
 
544
+    /**
545
+     * 考勤记录数据检查
546
+     *
547
+     * @param dto
548
+     * @param userInfo
549
+     */
550
+    private void checkData(AttendanceRecordDTO dto, UserInfo userInfo) {
551
+        AttendanceRecordReq dtoReq = new AttendanceRecordReq();
552
+        dtoReq.setUserId(userInfo.getUserId());
553
+        List<AttendanceRecord> attendanceRecords = queryRecordListNew(dtoReq);
554
+        if (StrUtil.equals("1", dto.getCheckInType())) {
555
+            if (CollectionUtil.isNotEmpty(attendanceRecords) && attendanceRecords.get(0).getCheckOutTime() == null) {
556
+                throw new ServiceException("请勿重复打上班卡");
557
+            }
558
+        } else if (StrUtil.equals("2", dto.getCheckInType())) {
559
+            if (CollectionUtil.isEmpty(attendanceRecords) || attendanceRecords.get(0).getCheckOutTime() != null) {
560
+                throw new ServiceException("请勿重复打下班卡");
561
+            }
562
+        } else {
563
+            throw new ServiceException("打卡类型错误");
564
+        }
565
+    }
566
+
567
+    /**
568
+     * 查询考勤记录列表
569
+     */
570
+    private List<AttendanceRecord> queryRecordListNew(AttendanceRecordReq dto) {
571
+        AttendanceRecord recordSearch = new AttendanceRecord();
572
+        recordSearch.setUserId(dto.getUserId());
573
+        return attendanceRecordMapper.selectAttendanceRecordList(recordSearch);
574
+    }
479 575
 
576
+    /**
577
+     * 查询可打卡类型:1=签到,2=签退
578
+     */
579
+    @Override
580
+    public String getRecordType() {
581
+        AttendanceRecordReq dtoReq = new AttendanceRecordReq();
582
+        dtoReq.setUserId(SecurityUtils.getUserId());
583
+        List<AttendanceRecord> attendanceRecords = queryRecordListNew(dtoReq);
584
+        if (CollectionUtil.isEmpty(attendanceRecords)) {
585
+            return "1";
586
+        }
587
+        if (attendanceRecords.get(0).getCheckOutTime() != null) {
588
+            return "1";
589
+        }
590
+        return "2";
591
+    }
480 592
 }

+ 66 - 32
airport-attendance/src/main/java/com/sundot/airport/attendance/service/impl/AttendanceTeamUserRecordServiceImpl.java

@@ -19,6 +19,7 @@ import org.springframework.stereotype.Service;
19 19
 import com.sundot.airport.attendance.mapper.AttendanceTeamUserRecordMapper;
20 20
 import com.sundot.airport.attendance.domain.AttendanceTeamUserRecord;
21 21
 import com.sundot.airport.attendance.service.IAttendanceTeamUserRecordService;
22
+import org.springframework.transaction.annotation.Transactional;
22 23
 
23 24
 import static com.sundot.airport.common.utils.SecurityUtils.getUsername;
24 25
 
@@ -64,26 +65,32 @@ public class AttendanceTeamUserRecordServiceImpl implements IAttendanceTeamUserR
64 65
     @Override
65 66
     public List<AttendanceTeamUserRecord> selectAttendanceTeamUserRecordList(AttendanceTeamUserRecord attendanceTeamUserRecord) {
66 67
         // 如果指定了考勤日期,则调整查询时间范围
67
-        if (attendanceTeamUserRecord.getAttendanceDate() != null) {
68
-            try {
69
-                String display = configService.selectConfigByKey("attendance.team.user.record.display.end.time");
70
-                Date date = getDate(new Date(), display);
71
-                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
72
-                String baseDateStr = sdf.format(date);
73
-                String nextDateStr = sdf.format(DateUtils.addDays(date, 1));
74
-
75
-                // 显示时间范围:当日7:00到次日9:00
76
-                Date displayStartDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(baseDateStr + " " + configService.selectConfigByKey("attendance.team.user.record.start.time"));
77
-                Date displayEndDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(nextDateStr + " " + display);
78
-
79
-                // 设置查询时间范围
80
-                attendanceTeamUserRecord.setAttendanceDateStart(displayStartDate);
81
-                attendanceTeamUserRecord.setAttendanceDateEnd(displayEndDate);
82
-                // 清除原始的attendanceDate,避免冲突
83
-                attendanceTeamUserRecord.setAttendanceDate(null);
84
-            } catch (Exception e) {
85
-                throw new ServiceException("日期格式化错误");
86
-            }
68
+//        if (attendanceTeamUserRecord.getAttendanceDate() != null) {
69
+//            try {
70
+//                String display = configService.selectConfigByKey("attendance.team.user.record.display.end.time");
71
+//                Date date = getDate(new Date(), display);
72
+//                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
73
+//                String baseDateStr = sdf.format(date);
74
+//                String nextDateStr = sdf.format(DateUtils.addDays(date, 1));
75
+//
76
+//                // 显示时间范围:当日7:00到次日9:00
77
+//                Date displayStartDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(baseDateStr + " " + configService.selectConfigByKey("attendance.team.user.record.start.time"));
78
+//                Date displayEndDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(nextDateStr + " " + display);
79
+//
80
+//                // 设置查询时间范围
81
+//                attendanceTeamUserRecord.setAttendanceDateStart(displayStartDate);
82
+//                attendanceTeamUserRecord.setAttendanceDateEnd(displayEndDate);
83
+//                // 清除原始的attendanceDate,避免冲突
84
+//                attendanceTeamUserRecord.setAttendanceDate(null);
85
+//            } catch (Exception e) {
86
+//                throw new ServiceException("日期格式化错误");
87
+//            }
88
+//        }
89
+        attendanceTeamUserRecord.setCreateBy(getUsername());
90
+        attendanceTeamUserRecord.setCheckInType("1");
91
+        Date maxDate = attendanceTeamUserRecordMapper.selectMaxDateBy(attendanceTeamUserRecord);
92
+        if (maxDate != null) {
93
+            attendanceTeamUserRecord.setAttendanceDate(maxDate);
87 94
         }
88 95
         return attendanceTeamUserRecordMapper.selectAttendanceTeamUserRecordList(attendanceTeamUserRecord);
89 96
     }
@@ -141,6 +148,7 @@ public class AttendanceTeamUserRecordServiceImpl implements IAttendanceTeamUserR
141 148
      * @param attendanceTeamUserRecord
142 149
      * @return
143 150
      */
151
+    @Transactional(rollbackFor = Exception.class)
144 152
     @Override
145 153
     public int insertAttendanceTeamUserRecordList(List<AttendanceTeamUserRecord> attendanceTeamUserRecord) {
146 154
         List<String> userName = new ArrayList<>();
@@ -161,27 +169,43 @@ public class AttendanceTeamUserRecordServiceImpl implements IAttendanceTeamUserR
161 169
             // 构建查询条件
162 170
             AttendanceTeamUserRecord searchRecord = new AttendanceTeamUserRecord();
163 171
             searchRecord.setUserId(record.getUserId());
164
-            searchRecord.setAttendanceDateStart(startDate);
165
-            searchRecord.setAttendanceDateEnd(endDate);
172
+//            searchRecord.setAttendanceDateStart(startDate);
173
+//            searchRecord.setAttendanceDateEnd(endDate);
174
+            searchRecord.setAttendanceDate(record.getAttendanceDate());
175
+            searchRecord.setCheckInType("1");
166 176
 
167 177
             // 查询时间范围内的记录
168 178
             List<AttendanceTeamUserRecord> existingRecords = attendanceTeamUserRecordMapper
169 179
                     .selectAttendanceTeamUserRecordList(searchRecord);
170 180
 
171 181
             // 检查是否存在班次周期内的重复记录(当日7:00 - 次日7:00)
172
-            if (hasRecordInShiftPeriod(existingRecords, record.getAttendanceDate())) {
173
-                // 获取第一个重复记录的用户名
174
-                AttendanceTeamUserRecord firstDuplicate = existingRecords.stream()
175
-                        .filter(r -> isWithinShiftPeriod(r.getCreateTime(), new Date()))
176
-                        .findFirst()
177
-                        .orElse(null);
178
-                if (firstDuplicate != null) {
179
-                    userName.add(firstDuplicate.getUserName());
180
-                }
182
+//            if (hasRecordInShiftPeriod(existingRecords, record.getAttendanceDate())) {
183
+//                // 获取第一个重复记录的用户名
184
+//                AttendanceTeamUserRecord firstDuplicate = existingRecords.stream()
185
+//                        .filter(r -> isWithinShiftPeriod(r.getCreateTime(), new Date()))
186
+//                        .findFirst()
187
+//                        .orElse(null);
188
+//                if (firstDuplicate != null) {
189
+//                    userName.add(firstDuplicate.getUserName());
190
+//                }
191
+//            }
192
+
193
+            if (!CollectionUtil.isEmpty(existingRecords)) {
194
+                userName.add(existingRecords.get(0).getUserName());
181 195
             }
182 196
         });
183 197
         if (userName.size() > 0) {
184
-            throw new ServiceException(StrUtil.format("用户{}在当前班次周期内已维护", JSONArray.toJSONString(userName)));
198
+            throw new ServiceException(StrUtil.format("用户【{}】在当前班次周期内已维护", String.join(",", userName)));
199
+        }
200
+
201
+        // 先删除后新增
202
+        AttendanceTeamUserRecord oldSearchRecord = new AttendanceTeamUserRecord();
203
+        oldSearchRecord.setCreateBy(attendanceTeamUserRecord.get(0).getCreateBy());
204
+        oldSearchRecord.setAttendanceDate(attendanceTeamUserRecord.get(0).getAttendanceDate());
205
+        List<AttendanceTeamUserRecord> oldList = attendanceTeamUserRecordMapper.selectAttendanceTeamUserRecordList(oldSearchRecord);
206
+        if (CollectionUtil.isNotEmpty(oldList)) {
207
+            Long[] oldIdArray = oldList.stream().map(AttendanceTeamUserRecord::getId).toArray(Long[]::new);
208
+            attendanceTeamUserRecordMapper.deleteAttendanceTeamUserRecordByIds(oldIdArray);
185 209
         }
186 210
         return attendanceTeamUserRecordMapper.insertAttendanceTeamUserRecordList(attendanceTeamUserRecord);
187 211
     }
@@ -265,4 +289,14 @@ public class AttendanceTeamUserRecordServiceImpl implements IAttendanceTeamUserR
265 289
         }
266 290
     }
267 291
 
292
+    /**
293
+     * 批量删除考勤班组成员
294
+     *
295
+     * @param ids 需要删除的考勤班组成员主键
296
+     * @return 结果
297
+     */
298
+    @Override
299
+    public int deleteAttendanceTeamUserRecordByIds(Long[] ids) {
300
+        return attendanceTeamUserRecordMapper.deleteAttendanceTeamUserRecordByIds(ids);
301
+    }
268 302
 }

+ 12 - 2
airport-attendance/src/main/resources/mapper/attendance/AttendancePostRecordMapper.xml

@@ -40,6 +40,7 @@
40 40
         <result property="statusDesc" column="status_desc"/>
41 41
         <result property="positionCode" column="position_code"/>
42 42
         <result property="positionName" column="position_name"/>
43
+        <result property="locked" column="locked"/>
43 44
     </resultMap>
44 45
 
45 46
     <sql id="selectAttendancePostRecordVo">
@@ -77,7 +78,8 @@
77 78
                remark,
78 79
                status_desc,
79 80
                position_code,
80
-               position_name
81
+               position_name,
82
+               locked
81 83
         from attendance_post_record
82 84
     </sql>
83 85
 
@@ -87,6 +89,7 @@
87 89
         <where>
88 90
             <if test="tenantId != null  and tenantId != ''">and tenant_id = #{tenantId}</if>
89 91
             <if test="revision != null ">and revision = #{revision}</if>
92
+            <if test="createBy != null ">and create_by = #{createBy}</if>
90 93
             <if test="userId != null ">and user_id = #{userId}</if>
91 94
             <if test="channelCode != null  and channelCode != ''">and channel_code = #{channelCode}</if>
92 95
             <if test="shiftCode != null  and shiftCode != ''">and shift_code = #{shiftCode}</if>
@@ -102,7 +105,7 @@
102 105
             <if test="checkOutTime != null ">and check_out_time = #{checkOutTime}</if>
103 106
             <if test="workDuration != null ">and work_duration = #{workDuration}</if>
104 107
             <if test="status != null  and status != ''">and status = #{status}</if>
105
-            <if test="attendanceDate != null ">and attendance_date = #{attendanceDate}</if>
108
+            <if test="attendanceDate != null ">and DATE(attendance_date) = DATE(#{attendanceDate})</if>
106 109
             <if test="attendanceTeamId != null ">and attendance_team_id = #{attendanceTeamId}</if>
107 110
             <if test="overDuration != null ">and over_duration = #{overDuration}</if>
108 111
             <if test="attendanceTeamName != null  and attendanceTeamName != ''">and attendance_team_name like
@@ -274,6 +277,7 @@
274 277
             <if test="statusDesc != null and statusDesc != ''">status_desc = #{statusDesc},</if>
275 278
             <if test="positionCode != null">position_code = #{positionCode},</if>
276 279
             <if test="positionName != null">position_name = #{positionName},</if>
280
+            <if test="locked != null">locked = #{locked},</if>
277 281
         </trim>
278 282
         where id = #{id}
279 283
     </update>
@@ -404,4 +408,10 @@
404 408
             )
405 409
         </where>
406 410
     </select>
411
+
412
+    <select id="selectMaxDateByUserId" resultType="java.util.Date">
413
+        select max(attendance_date) attendance_date
414
+        from attendance_post_record
415
+        where user_id = #{userId}
416
+    </select>
407 417
 </mapper>

+ 1 - 1
airport-attendance/src/main/resources/mapper/attendance/AttendanceRecordMapper.xml

@@ -87,7 +87,7 @@
87 87
             </if>
88 88
             <if test="userName != null  and userName != ''">and user_name like concat('%', #{userName}, '%')</if>
89 89
             <if test="remark != null  and remark != ''">and remark = #{remark}</if>
90
-            <if test="attendanceDate != null ">and attendance_date = #{attendanceDate}</if>
90
+            <if test="attendanceDate != null ">and DATE(attendance_date) = DATE(#{attendanceDate})</if>
91 91
             <if test="overDuration != null ">and over_duration = #{overDuration}</if>
92 92
             <if test="statusDesc != null  and statusDesc != ''">and status_desc = #{statusDesc}</if>
93 93
 

+ 37 - 6
airport-attendance/src/main/resources/mapper/attendance/AttendanceTeamUserRecordMapper.xml

@@ -5,6 +5,7 @@
5 5
 <mapper namespace="com.sundot.airport.attendance.mapper.AttendanceTeamUserRecordMapper">
6 6
 
7 7
     <resultMap type="AttendanceTeamUserRecord" id="AttendanceTeamUserRecordResult">
8
+        <result property="id" column="id"/>
8 9
         <result property="userId" column="user_id"/>
9 10
         <result property="userCode" column="user_code"/>
10 11
         <result property="userName" column="user_name"/>
@@ -18,10 +19,12 @@
18 19
         <result property="updateBy" column="update_by"/>
19 20
         <result property="updateTime" column="update_time"/>
20 21
         <result property="remark" column="remark"/>
22
+        <result property="checkInType" column="check_in_type"/>
21 23
     </resultMap>
22 24
 
23 25
     <sql id="selectAttendanceTeamUserRecordVo">
24
-        select user_id,
26
+        select id,
27
+               user_id,
25 28
                user_code,
26 29
                user_name,
27 30
                terminl_code,
@@ -33,7 +36,8 @@
33 36
                create_time,
34 37
                update_by,
35 38
                update_time,
36
-               remark
39
+               remark,
40
+               check_in_type
37 41
         from attendance_team_user_record
38 42
     </sql>
39 43
 
@@ -48,7 +52,7 @@
48 52
             <if test="terminlName != null  and terminlName != ''">and terminl_name like concat('%', #{terminlName},
49 53
                 '%')
50 54
             </if>
51
-            <if test="attendanceDate != null ">and attendance_date = #{attendanceDate}</if>
55
+            <if test="attendanceDate != null ">and DATE(attendance_date) = DATE(#{attendanceDate})</if>
52 56
             <!-- 添加时间范围查询支持 -->
53 57
             <if test="attendanceDateStart != null ">and create_time >= #{attendanceDateStart}</if>
54 58
             <if test="attendanceDateEnd != null ">and create_time &lt;= #{attendanceDateEnd}</if>
@@ -56,6 +60,7 @@
56 60
             <if test="attendanceTeamName != null  and attendanceTeamName != ''">and attendance_team_name like
57 61
                 concat('%', #{attendanceTeamName}, '%')
58 62
             </if>
63
+            <if test="checkInType != null and checkInType != ''">and check_in_type = #{checkInType}</if>
59 64
         </where>
60 65
         order by create_time desc
61 66
     </select>
@@ -82,6 +87,7 @@
82 87
             <if test="updateBy != null">update_by,</if>
83 88
             <if test="updateTime != null">update_time,</if>
84 89
             <if test="remark != null">remark,</if>
90
+            <if test="checkInType != null">check_in_type,</if>
85 91
         </trim>
86 92
         <trim prefix="values (" suffix=")" suffixOverrides=",">
87 93
             <if test="userId != null">#{userId},</if>
@@ -97,6 +103,7 @@
97 103
             <if test="updateBy != null">#{updateBy},</if>
98 104
             <if test="updateTime != null">#{updateTime},</if>
99 105
             <if test="remark != null">#{remark},</if>
106
+            <if test="checkInType != null">#{checkInType},</if>
100 107
         </trim>
101 108
     </insert>
102 109
 
@@ -115,8 +122,9 @@
115 122
             <if test="updateBy != null">update_by = #{updateBy},</if>
116 123
             <if test="updateTime != null">update_time = #{updateTime},</if>
117 124
             <if test="remark != null">remark = #{remark},</if>
125
+            <if test="checkInType != null">check_in_type = #{checkInType},</if>
118 126
         </trim>
119
-        where user_id = #{userId}
127
+        where id = #{id}
120 128
     </update>
121 129
 
122 130
     <delete id="deleteAttendanceTeamUserRecordByUserId" parameterType="Long">
@@ -145,7 +153,8 @@
145 153
     </insert>
146 154
 
147 155
     <select id="selectAttendanceTeamUserRecordByUserIdAndDateRange" resultMap="AttendanceTeamUserRecordResult">
148
-        SELECT tenant_id,
156
+        SELECT id,
157
+               tenant_id,
149 158
                revision,
150 159
                create_by,
151 160
                create_time,
@@ -159,12 +168,34 @@
159 168
                attendance_date,
160 169
                attendance_team_id,
161 170
                attendance_team_name,
162
-               remark
171
+               remark,
172
+               check_in_type
163 173
         FROM attendance_team_user_record
164 174
         WHERE user_id = #{userId}
165 175
           AND attendance_date >= #{startDate}
166 176
           AND attendance_date &lt;= #{endDate}
167 177
     </select>
168 178
 
179
+    <delete id="deleteAttendanceTeamUserRecordByIds" parameterType="String">
180
+        delete from attendance_team_user_record where id in
181
+        <foreach item="id" collection="array" open="(" separator="," close=")">
182
+            #{id}
183
+        </foreach>
184
+    </delete>
169 185
 
186
+    <select id="selectMaxDateBy" parameterType="AttendanceTeamUserRecord" resultType="java.util.Date">
187
+        select max(attendance_date) attendance_date
188
+        from attendance_team_user_record
189
+        <where>
190
+            <if test="checkInType != null and checkInType != ''">
191
+                and check_in_type = #{checkInType}
192
+            </if>
193
+            <if test="createBy != null and createBy != ''">
194
+                and create_by = #{createBy}
195
+            </if>
196
+            <if test="attendanceTeamId != null">
197
+                and attendance_team_id = #{attendanceTeamId}
198
+            </if>
199
+        </where>
200
+    </select>
170 201
 </mapper>