caozixuan 9 місяців тому
батько
коміт
ba4663d17a

+ 5 - 1
sop-api/src/main/java/com/qmth/sop/server/api/TBDingController.java

@@ -206,8 +206,12 @@ public class TBDingController {
     @ApiOperation(value = "考勤结果统计")
     @RequestMapping(value = "/ding_statistic", method = RequestMethod.POST)
     @ApiResponses({ @ApiResponse(code = 200, message = "查询成功", response = DingStatisticResult.class) })
-    public Result findDingStatistic(@ApiParam(value = "服务单元id", required = true) @RequestParam Long serviceId, @ApiParam(value = "sop单号") @RequestParam(required = false) String sopNo,
+    public Result findDingStatistic(@ApiParam(value = "服务单元id", required = false) @RequestParam(required = false) Long serviceId, @ApiParam(value = "sop单号") @RequestParam(required = false) String sopNo,
             @ApiParam(value = "打卡人id", required = true) @RequestParam String userId) {
+        // TODO: 2024/9/5 测试用
+        if (!SystemConstant.longNotNull(serviceId)){
+            serviceId = 1830811198218076162L;
+        }
         return ResultUtil.ok(tBDingService.findDingStatistic(serviceId, sopNo, SystemConstant.convertIdToLong(userId)));
     }
 

+ 12 - 2
sop-business/src/main/java/com/qmth/sop/business/service/TBDingStatisticService.java

@@ -19,11 +19,12 @@ public interface TBDingStatisticService extends IService<TBDingStatistic> {
     /**
      * 根据sopNo和用户档案id查询sop统计级路
      *
+     * @param serviceId      服务单元id
      * @param sopNo          sopNo
      * @param userArchivesId 用户档案id
      * @return 打卡统计记录
      */
-    TBDingStatistic findBySopNoAndUserArchivesId(String sopNo, Long userArchivesId);
+    TBDingStatistic findBySopNoAndUserArchivesId(Long serviceId, String sopNo, Long userArchivesId);
 
     /**
      * 构建考勤统计 (sop计划时间变更 -> 更新打卡统计时间)
@@ -46,11 +47,20 @@ public interface TBDingStatisticService extends IService<TBDingStatistic> {
     void buildDingStatistic();
 
     /**
-     * 构建考勤统计 (构建打卡统计信息)
+     * 构建考勤统计 (构建打卡统计信息) -- 正常工程师
      *
      * @param sopNo         sopNo
      * @param datasource    数据源
      * @param requestUserId 创建人
      */
     void buildDingStatisticBySop(String sopNo, List<TBDing> datasource, Long requestUserId);
+
+    /**
+     * 构建考勤统计 (构建打卡统计信息) -- 区域协调人身份
+     *
+     * @param serviceId     服务单元id
+     * @param datasource    数据源
+     * @param requestUserId 创建人
+     */
+    void buildDingStatisticByService(Long serviceId, List<TBDing> datasource, Long requestUserId);
 }

+ 4 - 3
sop-business/src/main/java/com/qmth/sop/business/service/TBDingSubmitService.java

@@ -112,9 +112,10 @@ public interface TBDingSubmitService extends IService<TBDingSubmit> {
     /**
      * 查询考勤设置是否可以更新
      *
-     * @param sopNo  sopNo
-     * @param userId 打卡用户id
+     * @param serviceId serviceId
+     * @param sopNo     sopNo
+     * @param userId    打卡用户id
      * @return true: 可以更新,false: 不可以更新
      */
-    boolean canUpdateDingSetting(String sopNo, Long userId);
+    boolean canUpdateDingSetting(Long serviceId, String sopNo, Long userId);
 }

+ 1 - 1
sop-business/src/main/java/com/qmth/sop/business/service/impl/TBDingApplyServiceImpl.java

@@ -147,7 +147,7 @@ public class TBDingApplyServiceImpl extends ServiceImpl<TBDingApplyMapper, TBDin
         tfCustomFlowEntity.setObjId(tbDingApply.getId());
         tfCustomFlowEntityService.updateById(tfCustomFlowEntity);
 
-        if (!tbDingSubmitService.canUpdateDingSetting(sopNo, sysUser.getId())) {
+        if (!tbDingSubmitService.canUpdateDingSetting(serviceId, sopNo, sysUser.getId())) {
             throw ExceptionResultEnum.ERROR.exception("考勤已提交,不能申请补卡");
         }
         TBUserArchives tbUserArchives = tbUserArchivesService.findByUserId(sysUser.getId());

+ 181 - 13
sop-business/src/main/java/com/qmth/sop/business/service/impl/TBDingStatisticServiceImpl.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.sop.business.bean.dto.RegionCoordinatorDingTimeDto;
 import com.qmth.sop.business.bean.result.CrmProjectResult;
 import com.qmth.sop.business.bean.result.UserArchivesResult;
 import com.qmth.sop.business.entity.*;
@@ -59,8 +60,15 @@ public class TBDingStatisticServiceImpl extends ServiceImpl<TBDingStatisticMappe
     private TBDingSubmitService tbDingSubmitService;
 
     @Override
-    public TBDingStatistic findBySopNoAndUserArchivesId(String sopNo, Long userArchivesId) {
-        return this.getOne(new QueryWrapper<TBDingStatistic>().lambda().eq(TBDingStatistic::getSopNo, sopNo).eq(TBDingStatistic::getUserArchivesId, userArchivesId).last(SystemConstant.LIMIT1));
+    public TBDingStatistic findBySopNoAndUserArchivesId(Long serviceId, String sopNo, Long userArchivesId) {
+        QueryWrapper<TBDingStatistic> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(TBDingStatistic::getServiceId, serviceId).eq(TBDingStatistic::getUserArchivesId, userArchivesId).last(SystemConstant.LIMIT1);
+        if (SystemConstant.strNotNull(sopNo)) {
+            queryWrapper.lambda().eq(TBDingStatistic::getSopNo, sopNo);
+        } else {
+            queryWrapper.lambda().isNull(TBDingStatistic::getSopNo);
+        }
+        return this.getOne(queryWrapper);
     }
 
     @Override
@@ -132,6 +140,22 @@ public class TBDingStatisticServiceImpl extends ServiceImpl<TBDingStatisticMappe
             log.error(String.format("sopNo[%s]未找到派单详情信息", sopNo));
             throw ExceptionResultEnum.ERROR.exception("未找到派单详情信息");
         }
+        Long scanActualStartTime = tbCrmDetail.getScanActualStartTime();
+        Long scanActualEndTime = tbCrmDetail.getScanActualEndTime();
+        Long markPaperActualStartTime = tbCrmDetail.getMarkPaperActualStartTime();
+        Long markPaperActualEndTime = tbCrmDetail.getMarkPaperActualEndTime();
+        if (Objects.nonNull(scanActualStartTime)) {
+            scanActualStartTime = DateDisposeUtils.getEarliestTime(scanActualStartTime);
+        }
+        if (Objects.nonNull(scanActualEndTime)) {
+            scanActualEndTime = DateDisposeUtils.getLatestTime(scanActualEndTime);
+        }
+        if (Objects.nonNull(markPaperActualStartTime)) {
+            markPaperActualStartTime = DateDisposeUtils.getEarliestTime(markPaperActualStartTime);
+        }
+        if (Objects.nonNull(markPaperActualEndTime)) {
+            markPaperActualEndTime = DateDisposeUtils.getLatestTime(markPaperActualEndTime);
+        }
 
         String crmNo = tbCrmDetail.getCrmNo();
         TBCrm tbCrm = tbCrmService.findByCrmNo(crmNo);
@@ -147,8 +171,7 @@ public class TBDingStatisticServiceImpl extends ServiceImpl<TBDingStatisticMappe
         Long markPaperStartTime = tbCrmDetail.getMarkPaperStartTime();
         Long markPaperEndTime = tbCrmDetail.getMarkPaperEndTime();
         // 该sop计划有效的日期
-
-        List<Long> userArchivesIdList = datasource.stream().filter(e -> Objects.equals(sopNo, e.getSopNo())).map(TBDing::getUserArchivesId).distinct().collect(Collectors.toList());
+        List<Long> userArchivesIdList = datasource.stream().map(TBDing::getUserArchivesId).distinct().collect(Collectors.toList());
 
         for (Long userArchivesId : userArchivesIdList) {
             UserArchivesResult tbUserArchives = tbUserArchivesService.findUserArchivesByArchivesIdORUserId(
@@ -159,10 +182,31 @@ public class TBDingStatisticServiceImpl extends ServiceImpl<TBDingStatisticMappe
             Long userId = tbUserArchives.getUserId();
             // 打卡数据集合
             List<TBDing> dingList = datasource.stream()
-                    .filter(e -> Objects.equals(sopNo, e.getSopNo()) && Objects.equals(userArchivesId, e.getUserArchivesId())).collect(Collectors.toList());
+                    .filter(e -> Objects.equals(userArchivesId, e.getUserArchivesId())).collect(Collectors.toList());
 
-            // 非异常打卡(打卡时间在计划时间范围内 以及 缺少签到或签退的)
-            List<TBDing> effectDingList = dingList.stream().filter(e -> !e.getDingException()).collect(Collectors.toList());
+            // 非异常打卡(打卡时间在计划时间范围内 以及签到签退完整的)
+            Long finalScanActualStartTime = scanActualStartTime;
+            Long finalScanActualEndTime = scanActualEndTime;
+            Long finalMarkPaperActualStartTime = markPaperActualStartTime;
+            Long finalMarkPaperActualEndTime = markPaperActualEndTime;
+            List<TBDing> effectDingList = dingList.stream()
+                    .filter(e -> !SystemConstant.isOneNull(e.getSignInTime(), e.getSignOutTime())).filter(e -> {
+                        // 在区协打卡时间范围内
+                        Long signInTime = e.getSignInTime();
+                        Long signOutTime = e.getSignOutTime();
+                        boolean inScan = false;
+                        boolean inMarkPaper = false;
+                        if (!SystemConstant.isOneNull(finalScanActualStartTime, finalScanActualEndTime)) {
+                            inScan = signInTime >= finalScanActualStartTime && signInTime <= finalScanActualEndTime
+                                    && signOutTime >= finalScanActualStartTime && signOutTime <= finalScanActualEndTime;
+                        }
+                        if (!SystemConstant.isOneNull(finalMarkPaperActualStartTime, finalMarkPaperActualEndTime)) {
+                            inMarkPaper = signInTime >= finalMarkPaperActualStartTime && signInTime <= finalMarkPaperActualEndTime
+                                    && signOutTime >= finalMarkPaperActualStartTime && signOutTime <= finalMarkPaperActualEndTime;
+                        }
+
+                        return inScan || inMarkPaper;
+                    }).collect(Collectors.toList());
 
             List<SopRoleTypeEnum> sopRoleTypeList = dingList.stream().flatMap(e -> {
                 String sopRoleTypeStr = e.getSopRoleType();
@@ -225,14 +269,14 @@ public class TBDingStatisticServiceImpl extends ServiceImpl<TBDingStatisticMappe
                     new QueryWrapper<TBDingApply>().lambda().eq(TBDingApply::getSopNo, sopNo).isNull(TBDingApply::getApprove));
 
             TBDingStatistic tbDingStatistic;
-            tbDingStatistic = this.findBySopNoAndUserArchivesId(sopNo, userArchivesId);
+            tbDingStatistic = this.findBySopNoAndUserArchivesId(serviceUnitId, sopNo, userArchivesId);
             if (Objects.isNull(tbDingStatistic)) {
                 // 新增考勤统计
                 tbDingStatistic = new TBDingStatistic();
                 tbDingStatistic.insertInfo(requestUserId);
             } else {
                 // 更新考勤统计
-                if (!tbDingSubmitService.canUpdateDingSetting(sopNo, userId)) {
+                if (!tbDingSubmitService.canUpdateDingSetting(serviceUnitId, sopNo, userId)) {
                     // 已提交的考勤不再计算了
                     continue;
                 }
@@ -248,10 +292,134 @@ public class TBDingStatisticServiceImpl extends ServiceImpl<TBDingStatisticMappe
             tbDingStatistic.setUserArchivesId(userArchivesId);
             tbDingStatistic.setUserArchivesName(tbUserArchives.getName());
             tbDingStatistic.setUserArchivesCode(tbUserArchives.getCode());
-            tbDingStatistic.setScanStartTime(scanStartTime);
-            tbDingStatistic.setScanEndTime(scanEndTime);
-            tbDingStatistic.setMarkPaperStartTime(markPaperStartTime);
-            tbDingStatistic.setMarkPaperEndTime(markPaperEndTime);
+            tbDingStatistic.setScanStartTime(tbCrmDetail.getScanActualStartTime());
+            tbDingStatistic.setScanEndTime(tbCrmDetail.getScanActualEndTime());
+            tbDingStatistic.setMarkPaperStartTime(tbCrmDetail.getMarkPaperActualStartTime());
+            tbDingStatistic.setMarkPaperEndTime(tbCrmDetail.getMarkPaperActualEndTime());
+            tbDingStatistic.setRoleName(sopRoleType);
+            tbDingStatistic.setSupplierId(tbUserArchives.getSupplierId());
+            tbDingStatistic.setSupplierName(tbUserArchives.getSupplierName());
+            tbDingStatistic.setActualDays(actualDays);
+            tbDingStatistic.setWeekdays(weekDays);
+            tbDingStatistic.setWeekends(weekends);
+            tbDingStatistic.setLegalHolidays(legalHolidays);
+            tbDingStatistic.setWorkHours(workHours);
+            tbDingStatistic.setViolationDays(violationDays);
+            tbDingStatistic.setDingExceptionCount(dingExceptionCount);
+            tbDingStatistic.setRemainCount(remainCount);
+            tbDingStatistic.setExceptionCount(exceptionCount);
+            tbDingStatisticList.add(tbDingStatistic);
+        }
+        this.saveOrUpdateBatch(tbDingStatisticList);
+    }
+
+    @Override
+    public void buildDingStatisticByService(Long serviceId, List<TBDing> datasource, Long requestUserId) {
+        RegionCoordinatorDingTimeDto dingTimeDto = tbDingService.findRegionCoordinatorDingTime();
+        Long startTime = dingTimeDto.getStartTime();
+        Long endTime = dingTimeDto.getEndTime();
+
+        List<TBDingStatistic> tbDingStatisticList = new ArrayList<>();
+        List<Long> userArchivesIdList = datasource.stream().map(TBDing::getUserArchivesId).distinct().collect(Collectors.toList());
+
+        for (Long userArchivesId : userArchivesIdList) {
+            UserArchivesResult tbUserArchives = tbUserArchivesService.findUserArchivesByArchivesIdORUserId(
+                    userArchivesId, null);
+            if (Objects.isNull(tbUserArchives)) {
+                throw ExceptionResultEnum.ERROR.exception("人员档案不存在");
+            }
+            Long userId = tbUserArchives.getUserId();
+            // 打卡数据集合
+            List<TBDing> dingList = datasource.stream()
+                    .filter(e -> Objects.equals(userArchivesId, e.getUserArchivesId())).collect(Collectors.toList());
+
+            // 非异常打卡(打卡时间在计划时间范围内 以及签到签退完整的)
+            List<TBDing> effectDingList = dingList.stream()
+                    .filter(e -> !SystemConstant.isOneNull(e.getSignInTime(), e.getSignOutTime())).filter(e -> {
+                        // 在区协打卡时间范围内
+                        Long signInTime = e.getSignInTime();
+                        Long signOutTime = e.getSignOutTime();
+                        return signInTime > startTime && signInTime < endTime && signOutTime > startTime && signOutTime < endTime;
+                    }).collect(Collectors.toList());
+
+            List<SopRoleTypeEnum> sopRoleTypeList = dingList.stream().flatMap(e -> {
+                String sopRoleTypeStr = e.getSopRoleType();
+                List<SopRoleTypeEnum> list = JSONArray.parseArray(sopRoleTypeStr, SopRoleTypeEnum.class);
+                return list.stream();
+            }).distinct().collect(Collectors.toList());
+            String sopRoleType = JSON.toJSONString(sopRoleTypeList);
+
+            // 根据非异常打卡数据统计
+            int actualDays = 0;
+            int weekDays = 0;
+            int weekends = 0;
+            int legalHolidays = 0;
+            BigDecimal workHours = BigDecimal.ZERO;
+            BigDecimal var = new BigDecimal(3600000);
+            for (TBDing effectDing : effectDingList) {
+                // 有效天数
+                actualDays++;
+                DingDateTypeEnum dateType = effectDing.getDateType();
+                switch (dateType) {
+                case WEEKEND:
+                    weekends++;
+                    break;
+                case LEGAL_HOLIDAYS:
+                    legalHolidays++;
+                    break;
+                default:
+                    weekDays++;
+                    break;
+                }
+                // 累计工时
+                workHours = workHours.add(
+                        new BigDecimal(effectDing.getSignOutTime() - effectDing.getSignInTime()).divide(var, 1,
+                                RoundingMode.HALF_UP));
+            }
+            // 违规工时
+            int violationDays = Math.toIntExact(
+                    dingList.stream().filter(e -> SystemConstant.isOneNull(e.getSignInTime(), e.getSignOutTime()))
+                            .count());
+            // 考勤异常数
+            int dingExceptionCount = 0;
+            for (TBDing tbDing : dingList) {
+                Long signInTime = tbDing.getSignInTime();
+                Long signOutTime = tbDing.getSignOutTime();
+                if (!SystemConstant.longNotNull(signInTime)) {
+                    dingExceptionCount++;
+                }
+                if (!SystemConstant.longNotNull(signOutTime)) {
+                    dingExceptionCount++;
+                }
+            }
+
+            // 剩余补卡数
+            int remainCount = tbDingService.findRemainCount(userId, serviceId, null);
+
+            // 待处理异常数
+            int exceptionCount = tbDingApplyService.count(
+                    new QueryWrapper<TBDingApply>().lambda().eq(TBDingApply::getServiceId, serviceId)
+                            .eq(TBDingApply::getCreateId, userId).isNull(TBDingApply::getApprove));
+
+            TBDingStatistic tbDingStatistic;
+            tbDingStatistic = this.findBySopNoAndUserArchivesId(serviceId, null, userArchivesId);
+            if (Objects.isNull(tbDingStatistic)) {
+                // 新增考勤统计
+                tbDingStatistic = new TBDingStatistic();
+                tbDingStatistic.insertInfo(requestUserId);
+            } else {
+                // 更新考勤统计
+                if (!tbDingSubmitService.canUpdateDingSetting(serviceId, null, userId)) {
+                    // 已提交的考勤不再计算了
+                    continue;
+                }
+                tbDingStatistic.updateInfo(requestUserId);
+            }
+            tbDingStatistic.setServiceId(serviceId);
+            tbDingStatistic.setUserId(userId);
+            tbDingStatistic.setUserArchivesId(userArchivesId);
+            tbDingStatistic.setUserArchivesName(tbUserArchives.getName());
+            tbDingStatistic.setUserArchivesCode(tbUserArchives.getCode());
             tbDingStatistic.setRoleName(sopRoleType);
             tbDingStatistic.setSupplierId(tbUserArchives.getSupplierId());
             tbDingStatistic.setSupplierName(tbUserArchives.getSupplierName());

+ 10 - 4
sop-business/src/main/java/com/qmth/sop/business/service/impl/TBDingSubmitServiceImpl.java

@@ -246,11 +246,17 @@ public class TBDingSubmitServiceImpl extends ServiceImpl<TBDingSubmitMapper, TBD
     }
 
     @Override
-    public boolean canUpdateDingSetting(String sopNo, Long userId) {
+    public boolean canUpdateDingSetting(Long serviceId, String sopNo, Long userId) {
         boolean result = true;
-        TBDingSubmit tbDingSubmit = this.getOne(
-                new QueryWrapper<TBDingSubmit>().lambda().eq(TBDingSubmit::getSopNo, sopNo)
-                        .eq(TBDingSubmit::getUserId, userId).last(SystemConstant.LIMIT1));
+        QueryWrapper<TBDingSubmit> queryWrapper = new QueryWrapper<>();
+        queryWrapper.lambda().eq(TBDingSubmit::getServiceId, serviceId).eq(TBDingSubmit::getUserId, userId).last(SystemConstant.LIMIT1);
+        if (SystemConstant.strNotNull(sopNo)) {
+            queryWrapper.lambda().eq(TBDingSubmit::getSopNo, sopNo);
+        } else {
+            queryWrapper.lambda().isNull(TBDingSubmit::getSopNo);
+        }
+
+        TBDingSubmit tbDingSubmit = this.getOne(queryWrapper);
         if (Objects.nonNull(tbDingSubmit)) {
             DingSubmitStatusEnum submitStatus = tbDingSubmit.getSubmitStatus();
             if (!DingSubmitStatusEnum.WILL_SUBMIT.equals(submitStatus) && !DingSubmitStatusEnum.AGREE_WITHDRAW.equals(

+ 6 - 0
sop-business/src/main/resources/db/log/caozixuan_update_log.sql

@@ -29,3 +29,9 @@ ALTER TABLE t_b_crm_detail
     ADD COLUMN mark_paper_actual_start_time BIGINT NULL COMMENT '阅卷开始实际时间\n研究生:评卷准备提交\n教务处:阅卷参数提交' AFTER scan_actual_end_time,
     ADD COLUMN mark_paper_actual_end_time BIGINT NULL COMMENT '阅卷结束实际时间\n研究生:设备入库提交\n教务处:评卷收尾检查提交' AFTER mark_paper_actual_start_time;
 
+ALTER TABLE t_b_ding_submit
+    CHANGE COLUMN sop_no sop_no VARCHAR(100) CHARACTER SET 'utf8mb4' NULL COMMENT 'sop单号' ;
+
+ALTER TABLE t_b_ding_statistic
+    CHANGE COLUMN sop_no sop_no VARCHAR(100) CHARACTER SET 'utf8mb4' NULL COMMENT 'sop单号' ,
+    CHANGE COLUMN crm_no crm_no VARCHAR(100) CHARACTER SET 'utf8mb4' NULL COMMENT 'crm单号' ;