浏览代码

Merge branch 'master' of http://47.100.37.243:10191/quyueting/manHourHousekeeper

yusm 3 周之前
父节点
当前提交
c60c6d8474
共有 15 个文件被更改,包括 206 次插入68 次删除
  1. 3 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/OvertimeAllowanceController.java
  2. 0 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  3. 4 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java
  4. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  5. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ExceptionInfos.java
  6. 19 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  7. 4 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  8. 72 38
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  9. 16 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/DateTimeUtil.java
  10. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ExceptionInfosMapper.xml
  11. 20 4
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue
  12. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/views/team/index.vue
  13. 51 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/newWeeklyCustomization.vue
  14. 5 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  15. 5 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue

+ 3 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/OvertimeAllowanceController.java

@@ -587,9 +587,9 @@ public class OvertimeAllowanceController {
             QueryWrapper<OvertimeAllowance> queryWrapper = new QueryWrapper<OvertimeAllowance>().eq("company_id", companyId);
             if (isAllowance) {
                 queryWrapper.ge("type", 0);
-            } else {
-                //只取考勤加班的数据
-                queryWrapper.eq("type", -1);
+            }  else {
+                //只取考勤加班的数据,type=-1或者白班加班,周末加班
+                queryWrapper.and(w->w.eq("type", -1).or().eq("type", 0).or().eq("type", 1).or().eq("type", 4));
             }
             // 根据userId查询
             if (!StringUtils.isEmpty(userId)) {

+ 0 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -3695,6 +3695,4 @@ public class ReportController {
         return msg;
     }
 
-
-
 }

+ 4 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java

@@ -556,7 +556,7 @@ public class UserCorpwxTimeController {
         if (localDate.isEqual(LocalDate.now())) {
             LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN);
             LocalDateTime end = LocalDateTime.of(localDate, LocalTime.MAX).withSecond(0).withNano(0);
-            wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getId(),start,end,false);
+            wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getCorpwxUserid(),start,end,false);
             List<UserCorpwxTime> changedCorpwxTimes = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>()
                     .eq("corpwx_userid", user.getCorpwxUserid()).eq("create_date", date));
 
@@ -595,7 +595,7 @@ public class UserCorpwxTimeController {
                 if (!hasTimeRecord) {
                     LocalDateTime start1 = LocalDateTime.of(localDate, LocalTime.MIN);
                     LocalDateTime end1 = LocalDateTime.of(localDate, LocalTime.MAX).withSecond(0).withNano(0);
-                    wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getId(),start1,end1,false);
+                    wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getCorpwxUserid(),start1,end1,false);
                     List<UserCorpwxTime> changedCorpwxTimes1 = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>()
                             .eq("corpwx_userid", user.getCorpwxUserid()).eq("create_date", date));
                     if (changedCorpwxTimes1.size() > 0) {
@@ -634,7 +634,7 @@ public class UserCorpwxTimeController {
             if (localDate.isEqual(LocalDate.now())) {
                 LocalDateTime start = LocalDateTime.of(localDate, LocalTime.MIN);
                 LocalDateTime end = LocalDateTime.of(localDate, LocalTime.MAX).withSecond(0).withNano(0);
-                wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getId(),start,end,false);
+                wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getCorpwxUserid(),start,end,false);
             } else if (localDate.isBefore(LocalDate.now())) {
                 LocalDateTime start = localDate.atTime(0,0,0);
                 LocalDateTime end = localDate.atTime(0,0,0);
@@ -649,7 +649,7 @@ public class UserCorpwxTimeController {
                     if (!hasTimeRecord) {
                         LocalDateTime start1 = LocalDateTime.of(localDate, LocalTime.MIN);
                         LocalDateTime end1 = LocalDateTime.of(localDate, LocalTime.MAX).withSecond(0).withNano(0);
-                        wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getId(),start1,end1,false);
+                        wxCorpInfoService.getUserPunchRecord(user.getCompanyId(),user.getCorpwxUserid(),start1,end1,false);
                     }
                 }
             }

+ 2 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java

@@ -2126,7 +2126,8 @@ public class WeiXinCorpController {
         } else {
             end = LocalDateTime.parse(startDate + " 23:59:59", dtf);
         }
-        return wxCorpInfoService.getUserPunchRecord(companyId, userId, start, end, true);
+        User user = userMapper.selectById(userId);
+        return wxCorpInfoService.getUserPunchRecord(companyId, user.getCorpwxUserid(), start, end, true);
     }
 
     //把没有打卡考勤记录的,通过每日打卡数据进行完善

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ExceptionInfos.java

@@ -61,8 +61,8 @@ public class ExceptionInfos extends Model<ExceptionInfos> {
     /**
      * 主表id
      */
-    @TableField("corpwxtime_id")
-    private Integer corpwxtimeId;
+//    @TableField("corpwxtime_id")
+//    private Integer corpwxtimeId;
 
     /**
      * 公司id

+ 19 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -2882,6 +2882,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
             } else {
                 list = projectMapper.getTimeCost(companyId, startDate, endDate, projectId, userIdList,deptIds,filterDeptIds,deptRelatedProjectIds, manProjectIds, inchargeUserIds,projectCategoryId,status);
             }
+            List<Project> allProjects = companyId.equals(Constant.AI_NOU_COMPANY_ID)?projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", companyId)):null;
 
             BigDecimal totalMoneyCost = BigDecimal.valueOf(0);
             List<List<String>> allList=null ;
@@ -2909,6 +2910,9 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 headList.add(MessageUtils.message("entry.projectId"));
                 //headList.add("项目名称");
                 headList.add(MessageUtils.message("entry.projectName"));
+                if (companyId == Constant.AI_NOU_COMPANY_ID) {
+                    headList.add("项目描述");
+                }
                 //headList.add("项目分类");
                 headList.add(MessageUtils.message("entry.projectType"));
                 //headList.add("人员");
@@ -2990,6 +2994,13 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                         List<String> rowData = new ArrayList<String>();
                         rowData.add((String)map.get("projectCode"));
                         rowData.add((String)map.get("project"));
+                        String projectDesc = "";
+                        if (companyId == Constant.AI_NOU_COMPANY_ID) {
+                            Integer pid = (Integer)map.get("id");
+                            Project curProject = allProjects.stream().filter(p->p.getId().equals(pid)).findFirst().orElse(null);
+                            projectDesc = curProject==null?"":curProject.getProjectDesc();
+                            rowData.add(projectDesc);
+                        }
                         rowData.add((String)map.get("categoryName"));
                         rowData.add("");
                         rowData.add("");
@@ -3009,7 +3020,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                                 rowData.add(((BigDecimal)map.get("costMoney")).toString());
                             }
                         }
-                        if (projectSum != null && projectSum == true) {
+                        if (projectSum != null && projectSum) {
                             allList.add(rowData);
                         }
                         //统计每个项目中的人员时间成本投入
@@ -3029,16 +3040,21 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                             double pTotalTime = 0;
                             BigDecimal pTotalMoney = new BigDecimal(0);
                             List<String> membRowData = new ArrayList<String>();
-                            if (projectSum == null || projectSum == false) {
+                            if (projectSum == null || !projectSum) {
                                 membRowData.add((String)map.get("projectCode"));
                                 membRowData.add((String)map.get("project"));
+                                if (companyId == Constant.AI_NOU_COMPANY_ID) {
+                                    membRowData.add(projectDesc);
+                                }
                                 membRowData.add((String)map.get("categoryName"));
 
                             } else {
                                 membRowData.add("");
                                 membRowData.add("");
+                                if (companyId == Constant.AI_NOU_COMPANY_ID) {
+                                    membRowData.add("");
+                                }
                                 membRowData.add("");
-
                             }
                             Department dept = null;
                             Department targetDept = new Department();

+ 4 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -15989,7 +15989,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             }
             userMap.put("yearTotalOvertime", yearTotalOvertime);
             userMap.put("yearTotalLeavetime", yearTotalLeavetime);
-            userMap.put("remainingLeavetime", yearTotalOvertime - yearTotalLeavetime);
+            double remainingLeavetime = yearTotalOvertime - yearTotalLeavetime;
+            //四舍五入到小数点后1位
+            remainingLeavetime = Math.round(remainingLeavetime * 10) / 10.0;
+            userMap.put("remainingLeavetime", remainingLeavetime);
             // 移除内部使用的corpwxUserid字段(可选)
             userMap.remove("corpwxUserid");
             list.add(userMap);

+ 72 - 38
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java

@@ -202,6 +202,8 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
     private DepartmentMapper departmentMapper;
     @Autowired
     private UserCorpwxTimeService userCorpwxTimeService;
+    @Autowired
+    private ExceptionInfosService exceptionInfosService;
 
 
     //获取服务商provider_access_token
@@ -732,12 +734,18 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         System.out.println("获取企微考勤 startTime=" + startTime + ",endTime=" + endTime);
 
         int batchCount = 1;
-        int batchSize = 1;
+        int batchSize = 100;
         int totalLength = 1;
         List<String> corpwxUserIds = new ArrayList<>();
         if (userId == null) {
             //获取企业下的全部员工
-            List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).isNotNull("corpwx_userid").eq("is_active", 1).eq("report_status", 0));
+            QueryWrapper<User> queryWrapper = new QueryWrapper<User>().eq("company_id", companyId).isNotNull("corpwx_userid").eq("is_active", 1);
+            if (!(companyId == Constant.XI_HE_CHAO_DAO_COMPANY_ID || companyId == Constant.XI_HE_CHAO_DAO_JIA_XING_COMPANY_ID)) {
+                queryWrapper.eq("report_status", 0);//只取需要提醒填报的人员
+            }
+//            queryWrapper.eq("report_status", 1);
+            List<User> users = userMapper.selectList(queryWrapper);
+
             System.out.println("获取考勤记录users size==" + users.size()+", companyId="+companyId+", "+corpInfo.getCorpName());
             corpwxUserIds = users.stream().map(User::getCorpwxUserid).collect(Collectors.toList());
             totalLength = corpwxUserIds.size();
@@ -770,7 +778,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
     }
 
     @Override
-    public HttpRespMsg getUserPunchRecord(int companyId, String userId, LocalDateTime startDateTime, LocalDateTime endDateTime, boolean showLog) {
+    public HttpRespMsg getUserPunchRecord(int companyId, String corpwxUserid, LocalDateTime startDateTime, LocalDateTime endDateTime, boolean showLog) {
         HttpRespMsg msg = new HttpRespMsg();
         WxCorpInfo corpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", companyId));
         if (corpInfo == null) {
@@ -793,7 +801,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
             int batchSize = 100;
             int totalLength = 1;
             List<String> corpwxUserIds = new ArrayList<>();
-            if (userId == null) {
+            if (corpwxUserid == null) {
                 //获取企业下的全部员工
                 List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).isNotNull("corpwx_userid").eq("is_active", 1));
                 corpwxUserIds = users.stream().map(User::getCorpwxUserid).collect(Collectors.toList());
@@ -801,8 +809,8 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                 batchCount = totalLength / batchSize + (totalLength % batchSize == 0 ? 0 : 1);
             } else {
                 //指定获取员工
-                User user = userMapper.selectById(userId);
-                corpwxUserIds.add(user.getCorpwxUserid());
+//                User user = userMapper.selectById(userId);
+                corpwxUserIds.add(corpwxUserid);
             }
             //按批调用
             for (int i = 0; i < batchCount; i++) {
@@ -1067,7 +1075,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         String checkinOption = getCheckinOption(corpInfo, dataTime, objects);
         JSONObject optionObject = JSONObject.parseObject(checkinOption);
 //        showLog = true;
-        JSONArray  optionDatas= optionObject.getJSONArray("info");
+        JSONArray  optionDatas = optionObject.getJSONArray("info");
         String url = GET_CHECKIN_DATA.replace("ACCESS_TOKEN", getCorpAccessToken(corpInfo));
         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_JSON);
@@ -1285,12 +1293,19 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                 //遍历,计算加班时长
                 List<UserCorpwxTime> updateList = new ArrayList<>();
                 for (UserCorpwxTime time : userCorpwxTimes) {
-                    double overtime = DateTimeUtil.calculateOvertime(time.getStartTime(), time.getEndTime());
+                    double overtime = DateTimeUtil.calculateOvertime(time.getCreateDate(), time.getStartTime(), time.getEndTime());
                     if (overtime > 0) {
                         UserCorpwxTime copy = new UserCorpwxTime();
                         copy.setId(time.getId());
                         copy.setOtTime(overtime);
-                        copy.setOtStatus(1);
+                        if (time.getWorkHours() == 0) {
+                            //工作时长为0,但是计算的加班时长是有的,说明当天是请假了全天,之前有的加班时长需要更新为0
+                            copy.setOtTime(0.0);
+                            copy.setOtStatus(null);
+                        } else {
+                            copy.setOtTime(overtime);
+                            copy.setOtStatus(1);
+                        }
                         updateList.add(copy);
                     }
                 }
@@ -1572,6 +1587,20 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
             DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
             if (json.getIntValue("errcode") == 0) {
                 JSONArray datas = json.getJSONArray("datas");
+                List<ExceptionInfos> allExceptionInfoList = new ArrayList<>();
+                //将startTime转化为LocalDate, startTime是秒为单位的。
+                LocalDate startLocalDate = Instant.ofEpochSecond(startTime).atZone(ZoneId.systemDefault()).toLocalDate();
+                LocalDate endLocalDate = Instant.ofEpochSecond(endTime).atZone(ZoneId.systemDefault()).toLocalDate();
+                if (corpInfo.getCompanyId() == Constant.XI_HE_CHAO_DAO_COMPANY_ID || corpInfo.getCompanyId() == Constant.XI_HE_CHAO_DAO_JIA_XING_COMPANY_ID) {
+                    //先删除日期范围内的人员的所有异常记录
+                    exceptionInfosMapper.delete(new QueryWrapper<ExceptionInfos>().eq("company_id", corpInfo.getCompanyId()).between("create_date", startLocalDate, endLocalDate).in("corpwx_userid", Arrays.asList(objects)));
+                }
+                //统一查询已有数据记录
+                List<UserCorpwxTime> allExistingCorpwxTimeList = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>()
+                        .eq("company_id", corpInfo.getCompanyId()).between("create_date", startLocalDate, endLocalDate).in("corpwx_userid", Arrays.asList(objects)));
+
+                List<UserCorpwxTime> updateList = new ArrayList<>();
+                List<UserCorpwxTime> insertList = new ArrayList<>();
                 for (int i = 0; i < datas.size(); i++) {
                     JSONObject jsonObject = datas.getJSONObject(i);
                     JSONObject base_info = jsonObject.getJSONObject("base_info");
@@ -1638,6 +1667,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     UserCorpwxTime ct = new UserCorpwxTime();
                     ct.setCompanyId(corpInfo.getCompanyId());
                     ct.setCorpwxUserid(curUserid);
+                    ct.setName(name);
                     ct.setWxCorpid(corpInfo.getCorpid());
                     ct.setCreateDate(localDate);
                     //计算周几
@@ -1665,7 +1695,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     ct.setOutdoorTime(0.0);
                     if (isCrossDay) {
                         //直接用cardTime作为工作时长
-                        ct.setName(name);
                         ct.setWorkHours(DateTimeUtil.getHoursFromDouble(ct.getCardTime()));
                     }
                     //工作日或者非工作日有打卡/加班,需要校正请假,外出的时长数据
@@ -1966,7 +1995,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                                 timeDelta = ct.getCardTime();
                             }
                         }
-                        ct.setName(name);
                         //解析请假和外出的情况
                         JSONArray sp_items = jsonObject.getJSONArray("sp_items");
                         for (int j = 0; j < sp_items.size(); j++) {
@@ -2222,8 +2250,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         }
                     }
 
-                    List<UserCorpwxTime> itemList = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>().eq("corpwx_userid", curUserid)
-                            .eq("create_date", localDate));
+                    List<UserCorpwxTime> itemList = allExistingCorpwxTimeList.stream().filter(t -> t.getCorpwxUserid().equals(curUserid) && t.getCreateDate().equals(localDate)).collect(Collectors.toList());
                     //有工作时长或者打卡时长或者请假时长,外出时长,都算有效时间
                     boolean hasTimeRecord = ct.getWorkHours() > 0 || ct.getCardTime() >= 1.0 || ct.getAskLeaveTime() > 0 || ct.getOutdoorTime() > 0;
                     //对于赛元微电子,2025年4月1日之前的都不做处理,防止把数据覆盖掉
@@ -2234,40 +2261,48 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     if (itemList.size() > 0) {
                         UserCorpwxTime item = itemList.get(0);
                         if (item.getModifiedByAdmin() != null && item.getModifiedByAdmin() == 0) {//自动同步的数据可以更新,管理员手动修改的不能覆盖掉
-                            if (itemList.size() > 1) {
-                                System.out.println("数据异常: "+curUserid+", "+localDate+". 存在"+itemList.size()+"条考勤,已自动删除多余的");
-                                for (int t=1; t < itemList.size(); t++) {
-                                    userCorpwxTimeMapper.deleteById(itemList.get(t).getId());
-                                }
-                            }
                             ct.setId(item.getId());
                             //之前有的时长不合法,或者新的有打卡时长
                             if (item.getWorkHours() <= 0 || hasTimeRecord) {
                                 if (showLog) System.out.println("更新考勤记录"+curUserid+", "+localDate);
-                                userCorpwxTimeMapper.updateById(ct);
+//                                userCorpwxTimeMapper.updateById(ct);
+                                updateList.add(ct);
                             }
                         }
                     } else {
                         if (hasTimeRecord) {
                             if (showLog) System.out.println("插入考勤记录"+curUserid+", "+localDate);
-                            userCorpwxTimeMapper.insert(ct);
-
-                        } else {
-                            //调用打卡详情去获取,弥补外出打卡且时间不在自动同步范围内的情况; 仅对工作日有效
-                            if (timeTypeService.isWorkDay(corpInfo.getCompanyId(), localDate, timeType, holidaySettings)) {
-                                User user = userMapper.selectOne(new QueryWrapper<User>().eq("corpwx_userid", curUserid));
-                                if (user != null) {
-                                    getUserPunchRecord(corpInfo.getCompanyId(), user.getId(), localDate.atTime(0,0,0),
-                                            localDate.atTime(23,59,59), showLog);
-                                }
-                            }
+//                            userCorpwxTimeMapper.insert(ct);
+                            insertList.add(ct);
                         }
+                        //影响性能,此处不再获取打卡,用户手动刷新单日接口本身有后续的获取打卡记录步骤,这里为冗余代码,需要去掉
+//                        else if (startTime == endTime){ //只在单日刷新考勤的情况下,才去获取打卡详情来计算
+//                            //调用打卡详情去获取,弥补外出打卡且时间不在自动同步范围内的情况; 仅对工作日有效
+//                            if (timeTypeService.isWorkDay(corpInfo.getCompanyId(), localDate, timeType, holidaySettings)) {
+//                                getUserPunchRecord(corpInfo.getCompanyId(), curUserid, localDate.atTime(0,0,0),
+//                                        localDate.atTime(23,59,59), showLog);
+//                            }
+//                        }
                     }
                     //保存异常情况记录
                     if (corpInfo.getCompanyId() == Constant.XI_HE_CHAO_DAO_JIA_XING_COMPANY_ID || corpInfo.getCompanyId() == Constant.XI_HE_CHAO_DAO_COMPANY_ID) {
-                        saveException(ct, jsonObject.getJSONArray("exception_infos"));
+//                        saveException(ct, jsonObject.getJSONArray("exception_infos"));
+                        List<ExceptionInfos> exceptionInfos = getExceptionInfos(ct, jsonObject.getJSONArray("exception_infos"));
+                        if (exceptionInfos != null) {
+                            allExceptionInfoList.addAll(exceptionInfos);
+                        }
                     }
                 }
+                //批量操作,效率高
+                if (!updateList.isEmpty()) {
+                    userCorpwxTimeService.updateBatchById(updateList);
+                }
+                if (!insertList.isEmpty()) {
+                    userCorpwxTimeService.saveBatch(insertList);
+                }
+                if (!allExceptionInfoList.isEmpty()) {
+                    exceptionInfosService.saveBatch(allExceptionInfoList);
+                }
             } else {
                 //TODO: 记录同步失败
 
@@ -2277,23 +2312,22 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         }
     }
 
-    private void saveException(UserCorpwxTime userCorpwxTime,JSONArray exceptionInfos) {
-        //删除当日的异常
-        exceptionInfosMapper.delete(new QueryWrapper<ExceptionInfos>().eq("create_date", userCorpwxTime.getCreateDate()).eq("corpwx_userid", userCorpwxTime.getCorpwxUserid()).eq("company_id", userCorpwxTime.getCompanyId()));
+    private List<ExceptionInfos> getExceptionInfos(UserCorpwxTime userCorpwxTime,JSONArray exceptionInfos) {
+        List<ExceptionInfos> list = null;
         if (exceptionInfos != null && exceptionInfos.size() > 0) {
             //重新保存
+            list = new ArrayList<>();
             for (int i = 0; i < exceptionInfos.size(); i++) {
                 ExceptionInfos exceptionInfos1 = exceptionInfos.getObject(i, ExceptionInfos.class);
-                exceptionInfos1.setCorpwxtimeId(userCorpwxTime.getId());
                 exceptionInfos1.setCreateDate(userCorpwxTime.getCreateDate());
                 exceptionInfos1.setCorpwxUserid(userCorpwxTime.getCorpwxUserid());
                 exceptionInfos1.setCompanyId(userCorpwxTime.getCompanyId());
-                exceptionInfosMapper.insert(exceptionInfos1);
+                list.add(exceptionInfos1);
             }
         }
+        return list;
     }
 
-
     private double convertDayTimeToHours(double d) {
         if (d == 12) {
             return 4.0;

+ 16 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/DateTimeUtil.java

@@ -342,6 +342,8 @@ public class DateTimeUtil {
     }
 
     public static void main(String[] args) {
+        LocalDate date = LocalDate.parse("2026-05-06", DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        System.out.println(calculateOvertime(date, "07:54","17:30"));
         // 测试用例验证 calculateHuaYanWorkHours 新规则(基准上班8:30,基准下班17:30)
         // 规则:
         // 1. 周一至周六:午间11:30-12:30扣1小时、17:30-18:00扣0.5小时;周日及法定假日不扣
@@ -391,8 +393,8 @@ public class DateTimeUtil {
         return dateTimeFormatter.format(lastDay);
     }
 
-    public static double calculateOvertime(String startTime, String endTime) {
-        String workEndTime = "17:30";
+    public static double calculateOvertime(LocalDate date, String startTime, String endTime) {
+        String workEndTime = "17:00";
         if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime) || endTime.compareTo(workEndTime) < 0) {
             return 0;
         }
@@ -406,9 +408,20 @@ public class DateTimeUtil {
                 endTime = endTime.substring(2).trim();
             }
         }
-        LocalDateTime start = LocalDateTime.parse(startDate + " " + workEndTime, dateTimeFormatter);
+        startTime = WorkDayCalculateUtils.isWorkDay(date)?workEndTime:startTime;//工作日按下班时间开始计算加班,非工作日全天都是加班。
+        if (startTime.compareTo("12:00") > 0 && startTime.compareTo("13:00") < 0) {
+            startTime = "13:00";//从午休结束开始算
+        }
+        LocalDateTime start = LocalDateTime.parse(startDate + " " + startTime, dateTimeFormatter);
+        if (endTime.compareTo("12:00") > 0 && endTime.compareTo("13:00") < 0) {
+            endTime = "12:00";//截止到午休开始
+        }
         LocalDateTime end = LocalDateTime.parse(endDate + " " + endTime, dateTimeFormatter);
         Duration duration = Duration.between(start, end);
+        //如果包含了午休,需要减去
+        if (startTime.compareTo("12:00") < 0 && endTime.compareTo("13:00") > 0) {
+            duration = duration.minus(Duration.ofMinutes(60));
+        }
         double overtime = duration.toMinutes() / 60.0;
         return getHalfHoursFromDouble(overtime);
     }

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ExceptionInfosMapper.xml

@@ -10,12 +10,12 @@
         <result column="duration" property="duration" />
         <result column="corpwx_userid" property="corpwxUserid" />
         <result column="create_date" property="createDate" />
-        <result column="corpwxtime_id" property="corpwxtimeId" />
+<!--        <result column="corpwxtime_id" property="corpwxtimeId" />-->
         <result column="company_id" property="companyId" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, count, exception, duration, corpwx_userid, create_date, corpwxtime_id, company_id
+        id, count, exception, duration, corpwx_userid, create_date, company_id
     </sql>
 </mapper>

+ 20 - 4
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -5879,7 +5879,7 @@
                             :openid="step.encryptedUserId"
                           ></TranslationOpenDataText>
                           <span v-else>{{ step.userName }}</span>
-                          {{ step.action }}
+                          {{ step.action }}{{ step.reason ? ' ' + step.reason : '' }}
                         </li>
                       </ul>
                     </div>
@@ -5922,7 +5922,9 @@
                   align="center"
                   label="审核状态"
                   width="120"
-                ></el-table-column>
+                >
+                
+              </el-table-column>
               </el-table>
             </template>
 
@@ -12619,7 +12621,7 @@ export default {
       // 正则匹配加密的userName (类似 woy9TkCAAAGMvDupx2H67MRafzoobBKw 或 woy9TkCAAAuM1qYIB1N_9ZaG__ucZS_Q 这样的字符串)
       // 匹配规则:在时间后面、在"提交了日报"或"审核通过了日报"等文字前面的加密字符串
       const regex =
-        /(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})\s+([a-zA-Z0-9_-]{32,})(提交了日报|审核通过了日报|驳回了日报|审核了日报)/;
+        /(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2})\s+([a-zA-Z0-9_-]{32,})(提交了日报|审核通过了日报|驳回了日报|审核了日报)(.*)/;
 
       // 处理每个审核步骤,返回结构化数据
       const processedSteps = workflowSteps
@@ -12629,11 +12631,12 @@ export default {
 
           const match = trimmedStep.match(regex);
           if (match) {
-            const [, dateTime, encryptedUserId, action] = match;
+            const [, dateTime, encryptedUserId, action, reason] = match;
             return {
               dateTime,
               encryptedUserId,
               action,
+              reason: reason ? reason.trim() : "",
               userName: "",
             };
           }
@@ -13024,6 +13027,19 @@ export default {
           this.calculateOvertimeLoading = false;
         });
     },
+    viewReason(item) {
+      this.postData('/report/getDenyReason', {
+        creatorId: item.creatorId,
+        createDate: item.createDate
+      })
+        .then((res) => {
+          this.viewDenyReasonDialog = true;
+          this.denyReason = res.data;
+        })
+        .finally(() => {
+          
+        });
+    },
   },
 };
 </script>

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/team/index.vue

@@ -428,7 +428,7 @@
                       <el-radio :label="0">否</el-radio>
                    </el-radio-group>
                 </el-form-item>
-                <el-form-item label="非一线部门" prop="flag" v-if="user.companyId==10 || user.companyId==5792">
+                <el-form-item label="非一线部门" prop="flag" v-if="user.companyId==8555 || user.companyId==5792">
                    <el-checkbox v-model="depForm.flag">部门员工为非一线员工</el-checkbox>
                 </el-form-item>
             </el-form>

+ 51 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/newWeeklyCustomization.vue

@@ -47,6 +47,15 @@
                             </el-select>
                         </template>
                     </el-table-column>
+                    <el-table-column label="任务" v-if="user.company && user.company.packageProject == 1 && !user.timeType.hideTask">
+                        <template slot-scope="scope">
+                            <el-select v-model="scope.row.taskId" size="small" placeholder="请选择任务" clearable filterable
+                                :disabled="scope.row.state == 1 || scope.row.state == 0 || !scope.row.canFill">
+                                <el-option v-for="item in scope.row.taskList" :key="item.taskId" :label="item.taskName" :value="item.taskId">
+                                </el-option>
+                            </el-select>
+                        </template>
+                    </el-table-column>
                     <el-table-column prop="content" label="工作内容" width="300">
                         <template slot-scope="scope">
                             <div>
@@ -291,7 +300,8 @@ export default {
                     workingTime: data.workingTime,
                     groupId: data.groupId || 0,
                     id: data.id || -1,
-                    degreeId: data.degreeId || -1
+                    degreeId: data.degreeId || -1,
+                    taskId: data.taskId || 0
                 }));
             let flag = false
             arr.forEach(item => {
@@ -376,6 +386,8 @@ export default {
             this.$set(this.weekTableData[index], 'projectAuditorName', '')
             this.$set(this.weekTableData[index], 'groupId', '')
             this.$set(this.weekTableData[index], 'projectName', '')
+            this.$set(this.weekTableData[index], 'taskId', null)
+            this.$set(this.weekTableData[index], 'taskList', [])
             if (projectId) {
                 var pName = this.projectList.filter(p=>p.id == projectId)[0].projectName + '-';
                 this.$set(this.weekTableData[index], 'projectName', pName)
@@ -385,7 +397,8 @@ export default {
                 this.$set(this.weekTableData[index],'degreeId',null)
                 this.$set(this.weekTableData[index],'multiDegrIdArray',null)
                 this.dimension(projectId, index);
-                
+                // 获取项目下的任务列表
+                this.getRecentTaskList(projectId, index);
             }
         },
         // 分组切换事件
@@ -452,9 +465,22 @@ export default {
             weekTableData.forEach(obj => {
                 obj.isDelete = sumSet.has(obj.dateTime) ? false : true;
                 sumSet.add(obj.dateTime);
+                // 初始化任务列表字段
+                if (!obj.taskList) {
+                    obj.taskList = [];
+                }
+                if (obj.taskId == 0) {
+                    obj.taskId = null;
+                }
             });
             this.weekTableData = weekTableData;
             console.log(weekTableData, '<========== weekTableData')
+            // 对已有项目的行,加载对应的任务列表
+            weekTableData.forEach((obj, idx) => {
+                if (obj.projectId) {
+                    this.getRecentTaskList(obj.projectId, idx);
+                }
+            });
         },
         // 获取项目列表
         async getProjectList() {
@@ -491,6 +517,27 @@ export default {
             }
             this.$set(this.weekTableData[index], 'taskGroups', data)
         },
+        // 获取项目下的近期任务列表
+        getRecentTaskList(projectId, index) {
+            if (!projectId) return;
+            const { isSubstitude } = this.weekParentData;
+            var param = {
+                projectId: projectId,
+                isSubstitude: isSubstitude ? 1 : 0,
+            };
+            this.http.post('/task/getRecentTask', param,
+                res => {
+                    if (res.code == "ok") {
+                        this.$set(this.weekTableData[index], 'taskList', res.data);
+                    } else {
+                        this.$message({ message: res.msg, type: 'error' });
+                    }
+                },
+                error => {
+                    this.$message({ message: error, type: 'error' });
+                }
+            );
+        },
         async dimension(projectId, index) {
             const { isSubstitude } = this.weekParentData;
             // let { data } = await this.getData('/task-group/listMyJoinGroup', { projectId, isSubstitude: isSubstitude ? 1 : 0 })
@@ -542,6 +589,8 @@ export default {
                 groupId: '',
                 groupList: [],
                 approverList: [],
+                taskId: null,
+                taskList: [],
                 canFill: 1,
                 isDelete: false
             })

+ 5 - 1
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue

@@ -159,7 +159,11 @@
                         @click="clickPickSubProject(index, item)" />
                     <van-popup v-model="item.showPickerSubProject" position="bottom">
                         <van-picker show-toolbar :columns="item.subProjectList" value-key="name" @confirm="choseSubProject"
-                            @cancel="item.showPickerSubProject = false; $forceUpdate();" />
+                            @cancel="item.showPickerSubProject = false; $forceUpdate();">
+                            <template #option="subItem">
+                                <span>{{ subItem.code ? subItem.code + ' - ' + subItem.name : subItem.name }}</span>
+                            </template>
+                        </van-picker>
                     </van-popup>
                     <van-field readonly name="extraField1" v-if="user.companyId == yuzhongCompId" clickable
                         :value="item.extraField1Name" label="角色选择" placeholder="请选择担任的角色" :disabled="!item.canEdit"

+ 5 - 1
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue

@@ -75,7 +75,11 @@
 
                         <van-popup v-model="item.showPickerSubProject" position="bottom">
                             <van-picker show-toolbar :columns="item.subProjectList" value-key="name" @confirm="choseSubProject" 
-                                @cancel="item.showPickerSubProject = false;$forceUpdate();" />
+                                @cancel="item.showPickerSubProject = false;$forceUpdate();">
+                                <template #option="subItem">
+                                    <span>{{ subItem.code ? subItem.code + ' - ' + subItem.name : subItem.name }}</span>
+                                </template>
+                            </van-picker>
                         </van-popup>
                         <!--任务分组 -->
                         <van-field  readonly  name="groupId" v-if="user.company.packageProject==1&&!user.timeType.hideGroup&&item.taskGroups != null && item.taskGroups.length > 0 && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1 && (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1)))" clickable