SignInServiceImpl.java
18.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
package com.ruoyi.in.service.impl;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import cn.hutool.core.collection.CollectionUtil;
import com.ruoyi.common.cache.SchedulingCache;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.uuid.Seq;
import com.ruoyi.common.utils.uuid.UUID;
import com.ruoyi.driver.domain.Driver;
import com.ruoyi.driver.mapper.DriverMapper;
import com.ruoyi.equipment.domain.Equipment;
import com.ruoyi.equipment.mapper.EquipmentMapper;
import com.ruoyi.pojo.DriverSignRecommendation;
import com.ruoyi.pojo.GlobalIndex;
import com.ruoyi.pojo.entity.DriverScheduling;
import com.ruoyi.pojo.response.SignInResponseVo;
import com.ruoyi.service.SchedulingService;
import com.ruoyi.service.ThreadJobService;
import com.ruoyi.utils.ConstDateUtil;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.in.mapper.SignInMapper;
import com.ruoyi.in.domain.SignIn;
import com.ruoyi.in.service.ISignInService;
import javax.annotation.Resource;
import static com.ruoyi.common.ConstDriverProperties.*;
import static com.ruoyi.common.ErrorTypeProperties.*;
import static com.ruoyi.common.ConstSignInConstSignInProperties.*;
import static com.ruoyi.common.redispre.GlobalRedisPreName.REDIS_SIGN_IN_DRIVER_ALCOHOL_OVERFLOW;
/**
* 签到Service业务层处理
*
* @author guzijian
* @date 2023-07-05
*/
@Service
public class SignInServiceImpl implements ISignInService {
private Logger log = LoggerFactory.getLogger(SignInServiceImpl.class);
@Resource
private SchedulingService schedulingService;
@Resource
private SchedulingCache schedulingCache;
@Autowired
private SignInMapper signInMapper;
@Autowired
private DriverMapper driverMapper;
@Autowired
private EquipmentMapper equipmentMapper;
@Autowired
private RedisCache redisCache;
@Resource
private ThreadJobService threadJobConfig;
/**
* 查询签到
*
* @param id 签到主键
* @return 签到
*/
@Override
public SignIn selectSignInById(Long id) {
return signInMapper.selectSignInById(id);
}
/**
* 查询签到列表
*
* @param signIn 签到
* @return 签到
*/
@Override
public List<SignInResponseVo> selectSignInList(SignInResponseVo signIn) {
SignInResponseVo vo = new SignInResponseVo(signIn);
List<SignInResponseVo> vos = signInMapper.selectSignInList(vo);
return vos;
}
/**
* 新增签到
*
* @param signIn 签到
* @return 结果
*/
@Override
public AjaxResult insertSignIn(SignIn signIn) {
// 查询员工信息
Driver driver = driverMapper.getDriverInfoByJobCode(signIn.getJobCode());
if (Objects.isNull(driver)) return AjaxResult.error("这个工号的员工不存在!");
// 查询地址
Equipment equipment = equipmentMapper.selectEquipmentByDeviceId(signIn.getDeviceId());
SignInResponseVo vo = getSignInResponseVo(driver, signIn, equipment);
signIn.setCreateTime(new Date());
signIn.setRemark("");
// 签到检查 酒精检查 超时检查 排班检查
GlobalIndex globalIndex = new GlobalIndex();
long now = System.currentTimeMillis();
List<DriverScheduling> dto = schedulingService.queryScheduling(signIn.getJobCode(), now);
if (checkSignIn(now, dto, globalIndex, signIn, driver)) {
signIn.setStatus(SIGN_IN_SUCCESS);
} else {
signIn.setStatus(SIGN_IN_FAIL);
signIn.setRemark(signIn.getRemark().replaceFirst(",$", "。"));
}
signIn.setIp(IpUtils.getIpAddr());
// base64转图片
// uploadImage(signIn);
signInMapper.insertSignIn(signIn);
// TODO redis 存储每个人的签到 用于后续考勤
// 驾驶人员二次签到酒精测试异常
if (PERSONNEL_POSTS_DRIVER.equals(driver.getPosts()) && SIGN_IN_FAIL.equals(signIn.getStatus()) && signIn.getRemark().contains(ALCOHOL_SIGN_IN_ERROR)) {
AjaxResult result = getAjaxResultByDriverSignInfo(signIn, vo);
if (!Objects.isNull(result)) {
return result;
}
}
if (SIGN_IN_FAIL.equals(signIn.getStatus())) {
return AjaxResult.success(SIGN_IN_ERROR + ": " + signIn.getRemark(), vo);
}
return AjaxResult.success(SIGN_IN_SUCCESS_STRING, vo);
}
/**
* 修改签到
*
* @param signIn 签到
* @return 结果
*/
@Override
public int updateSignIn(SignIn signIn) {
signIn.setUpdateTime(DateUtils.getNowDate());
return signInMapper.updateSignIn(signIn);
}
/**
* 批量删除签到
*
* @param ids 需要删除的签到主键
* @return 结果
*/
@Override
public int deleteSignInByIds(Long[] ids) {
return signInMapper.deleteSignInByIds(ids);
}
/**
* 删除签到信息
*
* @param id 签到主键
* @return 结果
*/
@Override
public int deleteSignInById(Long id) {
return signInMapper.deleteSignInById(id);
}
@Override
public AjaxResult addSignIn(SignIn signIn) throws IOException {
// 查询员工信息
Driver driver = driverMapper.getDriverInfoByJobCode(signIn.getJobCode());
if (Objects.isNull(driver)) return AjaxResult.warn("这个工号的员工不存在!");
// 查询地址
Equipment equipment = equipmentMapper.selectEquipmentByDeviceId(signIn.getDeviceId());
SignInResponseVo vo = getSignInResponseVo(driver,signIn, equipment);
signIn.setCreateTime(new Date());
signIn.setRemark("");
GlobalIndex globalIndex = new GlobalIndex();
long now = System.currentTimeMillis();
List<DriverScheduling> dto = schedulingService.queryScheduling(signIn.getJobCode(), now);
// 签到检查
if (checkSignIn(now, dto, globalIndex, signIn, driver)) {
signIn.setStatus(SIGN_IN_SUCCESS);
signIn.setExType(SIGN_NO_EX_NUM);
} else {
signIn.setStatus(SIGN_IN_FAIL);
signIn.setRemark(signIn.getRemark().replaceFirst(",$", "。"));
}
// base64转图片
signIn.setIp(IpUtils.getIpAddr());
uploadImage(signIn);
signInMapper.insertSignIn(signIn);
// 更新考勤
schedulingService.computedSignInBySignIn(dto, signIn, globalIndex);
// 测试用户特殊处理 TODO 更新签到记录
if (signIn.getJobCode().equals("700002") || signIn.getJobCode().equals("700001")) {
return AjaxResult.success(SIGN_IN_SUCCESS_STRING, vo);
}
// 驾驶人员二次签到酒精测试异常
if (PERSONNEL_POSTS_DRIVER.equals(driver.getPosts()) && SIGN_IN_FAIL.equals(signIn.getStatus()) && signIn.getRemark().contains(ALCOHOL_SIGN_IN_ERROR)) {
AjaxResult result = getAjaxResultByDriverSignInfo(signIn, vo);
if (!Objects.isNull(result)) {
return result;
}
}
return SIGN_IN_SUCCESS.equals(signIn.getStatus()) ? AjaxResult.success(SIGN_IN_SUCCESS_STRING, vo) : AjaxResult.success(SIGN_IN_ERROR + ":" + signIn.getRemark(), vo);
}
private SignInResponseVo getSignInResponseVo(Driver driver, SignIn signIn, Equipment equipment) {
SignInResponseVo vo = new SignInResponseVo(signIn);
if (Objects.isNull(equipment)) {
equipment = new Equipment();
}
vo.setAddress(equipment.getAddress());
vo.setDeviceId(signIn.getDeviceId());
vo.setSiteName(equipment.getSiteName());
vo.setName(driver.getPersonnelName());
vo.setPosts(driver.getPosts());
return vo;
}
private AjaxResult getAjaxResultByDriverSignInfo(SignIn signIn, SignInResponseVo vo) {
// TODO 待更换 缓存更新为当天签到记录
String key = REDIS_SIGN_IN_DRIVER_ALCOHOL_OVERFLOW + ConstDateUtil.formatDate("yyyyMMdd");
// 驾驶员酒精测试连续超标两次则提示换人
Integer count = redisCache.getCacheMapValue(key, signIn.getJobCode());
if (Objects.isNull(count) || count.equals(0)) {
count = 1;
redisCache.setCacheMapValue(key, signIn.getJobCode(), count);
} else {
count = redisCache.setIncrementMapValue(key, signIn.getJobCode(), 1);
}
if (SIGN_IN_ERROR_COUNT.compareTo(count) <= 0) {
return AjaxResult.success(SIGN_IN_ERROR + ": " + signIn.getRemark() + "酒精测试不通过" + count + "次请更换车辆驾驶员。", vo);
}
return null;
}
private boolean checkSignIn(long now, List<DriverScheduling> dto, GlobalIndex globalIndex, SignIn signIn, Driver driver) {
boolean result = true;
if (Objects.isNull(driver)) {
return true;
}
// TODO 排班校验 和签到超时校验 非司售未作
result = checkWorkDay(now, dto, globalIndex, signIn, driver.getPosts());
// 酒精测试校验 确定 且员工工种是驾驶员
if (ALCOHOL_FLAG_YES.equals(signIn.getAlcoholFlag()) && PERSONNEL_POSTS_DRIVER.equals(driver.getPosts())) {
if (new BigDecimal(20).compareTo(signIn.getAlcoholIntake()) <= 0) {
signIn.setRemark(signIn.getRemark() + ALCOHOL_SIGN_IN_ERROR + signIn.getAlcoholIntake().toString() + "mg/100ml。");
signIn.setExType(SIGN_ALCOHOL_EX_NUM);
result = false;
}
}
return result;
}
/**
* 排班和超时检查
*
* @param globalIndex
* @param signIn
* @param posts
* @return
*/
private boolean checkWorkDay(long now, List<DriverScheduling> dto, GlobalIndex globalIndex, SignIn signIn, String posts) {
Boolean result = true;
switch (posts) {
case "驾驶员":
case "售票员":
String key = ConstDateUtil.formatDate(now);
// 查询工号对应的排班 司售人员
log.debug("签到key:{},排班数据:{}", key, dto);
if (CollectionUtil.isEmpty(dto)) {
signIn.setRemark(signIn.getRemark() + WORK_DAY_ERROR);
signIn.setExType(SIGN_NO_SCHEDULING_EX_NUM);
result = false;
}
break;
default:
break;
}
// 超时校验司售人员
if (result) {
result = checkTimeOut(globalIndex, signIn, dto, posts, now);
if (!result) {
signIn.setExType(SIGN_TIME_OUT_EX_NUM);
}
}
return result;
}
private static Date getDayKey() {
Calendar calendar = Calendar.getInstance();
// 获取当前日期时间
Date currentDate = calendar.getTime();
// 设置时间为2:30 AM
calendar.set(Calendar.HOUR_OF_DAY, 2);
calendar.set(Calendar.MINUTE, 30);
calendar.set(Calendar.SECOND, 0);
// 判断当前时间是否在2:30 AM之前
if (currentDate.before(calendar.getTime())) {
// 当前时间在2:30 AM之前,将日期向前推一天
calendar.add(Calendar.DAY_OF_MONTH, -1);
}
// 获取结果日期对象
return calendar.getTime();
}
private boolean checkTimeOut(GlobalIndex globalIndex, SignIn signIn, List<DriverScheduling> dto, String posts, long now) {
// 那发车时间和到站时间作为开始上班的时间
if (PERSONNEL_POSTS_DRIVER.equals(posts) || PERSONNEL_POSTS_SALES.equals(posts)) {
return driverCheckTimeOut(globalIndex, signIn, dto, now);
} else {
return true;
}
}
private boolean driverCheckTimeOut(GlobalIndex globalIndex, SignIn signIn, List<DriverScheduling> dto, long now) {
try {
Boolean result = true;
DriverSignRecommendation currentScheduling = schedulingService.computedTheCurrentClosestTimestamp(dto, now, 0);
LocalDateTime endTime = ConstDateUtil.getLocalDateTimeByLongTime(currentScheduling.getTimestamps());
LocalDateTime nowTime = ConstDateUtil.getLocalDateTimeByLongTime(now);
long nowBetween = ChronoUnit.MINUTES.between(endTime, nowTime);
// 不在有效时间范围内,判断当前是否有签到记录 当前应签到还是签退
if (!(Math.abs(nowBetween) <= 60)) {
// 签到一个逻辑 当前无记录
if (currentScheduling.getBcType().equals(BC_TYPE_OUT) && Objects.isNull(currentScheduling.getSignInId())) {
DriverSignRecommendation lastClosestTimestamp = schedulingService.computedTheCurrentClosestTimestamp(dto, now, -1);
// 上一次无记|签到,type:签退 当前时间小于最近签到时间 -> 超时签退
if (nowBetween < -60L && Objects.isNull(lastClosestTimestamp.getSignInId()) && signIn.getType().equals(SIGN_OUT) && lastClosestTimestamp.getBcType().equals(BC_TYPE_IN)){
signIn.setRemark(SIGN_OUT_TIMEOUT);
globalIndex.setIndex(lastClosestTimestamp.getIndex());
result = false;
}
// 上一次无记无|签到,type:签退 当前时间小于最近签到时间 -> 超时签退
else if (nowBetween < -60L && Objects.isNull(lastClosestTimestamp.getSignInId()) && signIn.getType().equals(SIGN_OUT) && lastClosestTimestamp.getIndex().equals(currentScheduling.getIndex())) {
signIn.setRemark(SIGN_IN_TIMEOUT);
globalIndex.setIndex(currentScheduling.getIndex());
result = false;
}
// 当前最近无记录|签到,type:签到|签退 -> 签到超时给上
else {
signIn.setRemark(SIGN_IN_TIMEOUT);
globalIndex.setIndex(currentScheduling.getIndex());
result = false;
}
}
// 签退一个逻辑 当前无记录
if (currentScheduling.getBcType().equals(BC_TYPE_IN) && Objects.isNull(currentScheduling.getSignInId())) {
DriverSignRecommendation lastClosestTimestamp = schedulingService.computedTheCurrentClosestTimestamp(dto, now, -1);
// 上一次无记|签退,type:签到 当前时间小于最近签退时间 -> 签到异常
if (nowBetween < -60L && Objects.isNull(lastClosestTimestamp.getSignInId()) && signIn.getType().equals(SIGN_OUT)) {
signIn.setRemark(SIGN_IN_TIMEOUT);
globalIndex.setIndex(lastClosestTimestamp.getIndex());
result = false;
}
// 当前无记|签退 , type:签退|签到 ——> 签退异常
else {
signIn.setRemark(SIGN_OUT_TIMEOUT);
globalIndex.setIndex(currentScheduling.getIndex());
result = false;
}
}
// 签退|签到 当前有记录
if (!Objects.isNull(currentScheduling.getSignInId())) {
signIn.setRemark(currentScheduling.getBcType().equals(BC_TYPE_IN) ? SIGN_OUT_TIMEOUT : SIGN_IN_TIMEOUT);
globalIndex.setIndex(currentScheduling.getIndex());
result = false;
}
} else {
globalIndex.setIndex(currentScheduling.getIndex());
result = true;
}
return result;
} catch (Exception e) {
return true;
}
}
private void uploadImage(SignIn signIn) throws IOException {
String base64 = signIn.getImage();
// 图片路径
String filePath = RuoYiConfig.getUploadPath();
// 固定jpg文件
String fileName = "";
// 看是否带有base64前缀 有就判断文件类型 没有默认jpg
fileName = checkImageBase64Format(signIn.getImage());
fileName = extractFilename(fileName);
// 获取相对路径
String absPath = getAbsoluteFile(filePath, fileName).getAbsolutePath();
// 获取文件上传路径
String pathFileName = getPathFileName(filePath, fileName);
signIn.setImage(pathFileName);
log.info("开始上传签到图片");
// 异步上传文件
threadJobConfig.asyncStartUploadBase64Image(absPath, base64);
}
/**
* 获取相对路径名
*
* @param uploadDir
* @param fileName
* @return
* @throws IOException
*/
public File getAbsoluteFile(String uploadDir, String fileName) throws IOException {
File desc = new File(uploadDir + File.separator + fileName);
if (!desc.exists()) {
if (!desc.getParentFile().exists()) {
desc.getParentFile().mkdirs();
}
}
return desc;
}
/**
* 获取扩展文件名
*
* @param extendFileName
* @return
*/
public String extractFilename(String extendFileName) {
return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(),
FilenameUtils.getBaseName(UUID.randomUUID().toString().replace("-", "")), Seq.getId(Seq.uploadSeqType), extendFileName);
}
/**
* @param uploadDir
* @param fileName
* @return
* @throws IOException
*/
public String getPathFileName(String uploadDir, String fileName) throws IOException {
int dirLastIndex = RuoYiConfig.getProfile().length() + 1;
String currentDir = StringUtils.substring(uploadDir, dirLastIndex);
return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName;
}
/**
* 检查文件类型
*
* @param base64ImgData
* @return
*/
public static String checkImageBase64Format(String base64ImgData) {
byte[] b = Base64.getDecoder().decode(base64ImgData);
String type = "";
if (0x424D == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
type = "bmp";
} else if (0x8950 == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
type = "png";
} else {
type = "jpg";
}
// else if (0xFFD8 == ((b[0] & 0xff) << 8 | (b[1] & 0xff))) {
// type = "jpg";
// }
return type;
}
}