Procházet zdrojové kódy

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

yusm před 1 měsícem
rodič
revize
d5da8d10ce
16 změnil soubory, kde provedl 310 přidání a 107 odebrání
  1. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/OvertimeAllowanceController.java
  2. 5 6
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  3. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java
  4. 7 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WxCorpInfoController.java
  5. 6 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/OvertimeAllowance.java
  6. 0 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  7. 8 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserCorpwxTimeMapper.java
  8. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/WxCorpInfoService.java
  9. 38 13
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  10. 67 11
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  11. 53 30
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/DateTimeUtil.java
  12. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/OvertimeAllowanceMapper.xml
  13. 1 40
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  14. 42 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserCorpwxTimeMapper.xml
  15. 72 1
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue
  16. 5 1
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

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

@@ -96,7 +96,7 @@ public class OvertimeAllowanceController {
         Integer companyId = currentUser != null ? currentUser.getCompanyId() : null;
         QueryWrapper<Department> deptQueryWrapper = new QueryWrapper<Department>().eq("company_id", companyId);
         List<Department> departmentList = departmentService.list(deptQueryWrapper);
-        QueryWrapper<OvertimeAllowance> queryWrapper = new QueryWrapper<OvertimeAllowance>().eq("company_id", companyId);
+        QueryWrapper<OvertimeAllowance> queryWrapper = new QueryWrapper<OvertimeAllowance>().eq("company_id", companyId).eq("report_passed", true);
         //去掉非一线员工
         List<Department> nonFistLineDeptList = departmentList.stream().filter(Department::getFlag).collect(Collectors.toList());
         List<Integer> allNonFirstLineDeptIds = new ArrayList<>();

+ 5 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -1616,6 +1616,7 @@ public class ReportController {
                                         httpRespMsg.setError("尚无审核通过的加班单");
                                         return httpRespMsg;
                                     } else {
+                                        System.out.println("加班申请时长=="+overtime.getDuration());
                                         double applyOvertime = 1.0*overtime.getDuration()/3600;//转化为小时
                                         if (overTimeSum - applyOvertime > allowMoreThan) {
                                             httpRespMsg.setError("填报加班时长不得超过加班申请时长("+applyOvertime+"h)" + (allowMoreThan > 0.001? "+2h" : ""));
@@ -3634,6 +3635,10 @@ public class ReportController {
         return reportService.exportMissingCardTimeUserList(startDate, endDate,departmentId,  request);
     }
 
+    @RequestMapping("/exportExtraWorkHoursList")
+    public HttpRespMsg exportExtraWorkHoursList(String year, Integer departmentId, String userId, HttpServletRequest request) {
+        return reportService.exportExtraWorkHoursList(year,departmentId, userId, request);
+    }
     @RequestMapping("/getExtraWorkHoursList")
     public HttpRespMsg getExtraWorkHoursList(String year, Integer departmentId, String userId, HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
@@ -3641,12 +3646,6 @@ public class ReportController {
         return msg;
     }
 
-    @RequestMapping("/exportExtraWorkHoursList")
-    public HttpRespMsg exportExtraWorkHoursList(String year, Integer departmentId, String userId, HttpServletRequest request) {
-        return reportService.exportExtraWorkHoursList(year,departmentId, userId, request);
-    }
-
-
     //项目工时排名前十
     @RequestMapping("/getTop10ProjectReport")
     public HttpRespMsg getTop10ProjectReport(String ymonth) {

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

@@ -3148,4 +3148,6 @@ public class UserCorpwxTimeController {
     private double roundLeaveHours(double hours) {
         return Math.round(hours * 100.0) / 100.0;
     }
+
+
 }

+ 7 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WxCorpInfoController.java

@@ -203,5 +203,12 @@ public class WxCorpInfoController {
         msg.msg = "刷新餐补任务进行中...,请稍后检查报表, 大约需要" + time + "分钟";
         return msg;
     }
+
+    @RequestMapping("/calculateOvertime")
+    public HttpRespMsg calculateOvertime(Integer companyId, String ymonth) {
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.data = wxCorpInfoService.calculateOvertime(companyId, ymonth);
+        return msg;
+    }
 }
 

+ 6 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/OvertimeAllowance.java

@@ -101,6 +101,12 @@ public class OvertimeAllowance extends Model<OvertimeAllowance> {
     @TableField("work_hours")
     private Double workHours;
 
+    /**
+     * 日报是否审核通过
+     */
+    @TableField("report_passed")
+    private Boolean reportPassed;
+
     /**
      * 用户名称(非数据库字段)
      */

+ 0 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java

@@ -322,9 +322,6 @@ public interface ReportMapper extends BaseMapper<Report> {
     List<Map<String, Object>> getDepartmentAuditList(@Param("startDate") String startDate,
                                                      @Param("endDate") String endDate, @Param("userIds") List<String> userIds, @Param("projectId") Integer projectId);
 
-    List<HashMap> getExtraWorkHoursList(String year, Integer companyId, List<Integer> deptIds, String userId);
 
-    List<Map<String, Object>> getMonthlyOvertimeByYear(@Param("year") String year, @Param("companyId") Integer companyId, @Param("userIds") List<String> userIds);
 
-    List<Map<String, Object>> getMonthlyLeaveByYear(@Param("year") String year, @Param("companyId") Integer companyId, @Param("corpwxUserIds") List<String> corpwxUserIds);
 }

+ 8 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserCorpwxTimeMapper.java

@@ -2,8 +2,10 @@ package com.management.platform.mapper;
 
 import com.management.platform.entity.UserCorpwxTime;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.apache.ibatis.annotations.Param;
 
 import java.time.LocalDate;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -22,4 +24,10 @@ public interface UserCorpwxTimeMapper extends BaseMapper<UserCorpwxTime> {
     public List<Map> getUserDataRatioList(Integer companyId, List<LocalDate> dateList, String ymonth, List<Integer> deptIds, String userId);
 
     List<Map> selectByAsk(Integer companyId,String startDate,String endDate);
+
+    List<HashMap> getExtraWorkHoursList(String year, Integer companyId, List<Integer> deptIds, String userId);
+
+    List<Map<String, Object>> getMonthlyOvertimeByYear(@Param("year") String year, @Param("companyId") Integer companyId, @Param("corpwxUserIds") List<String> corpwxUserIds);
+
+    List<Map<String, Object>> getMonthlyLeaveByYear(@Param("year") String year, @Param("companyId") Integer companyId, @Param("corpwxUserIds") List<String> corpwxUserIds);
 }

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/WxCorpInfoService.java

@@ -110,4 +110,6 @@ public interface WxCorpInfoService extends IService<WxCorpInfo> {
 
     //根据员工提交日报触发,计算补贴
     public void handleUserAllowanceByReport(List<Report> reportList);
+
+    Object calculateOvertime(Integer companyId, String ymonth);
 }

+ 38 - 13
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -270,6 +270,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     private ProjectConstructionStageMapper projectConstructionStageMapper;
     @Resource
     private ReportProjectConstructionService reportProjectConstructionService;
+    @Resource
+    private OvertimeAllowanceMapper overtimeAllowanceMapper;
 
 
     //获取报告列表
@@ -3758,6 +3760,14 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             if (company.getId().equals(Constant.XI_HE_CHAO_DAO_COMPANY_ID)) {
                 LocalDate createDate = reportList.get(0).getCreateDate();
                 hrAutoApprove(creatorId, createDate);
+                //更新加班餐补表的日报状态
+                Report report = reportMapper.selectOne(new QueryWrapper<Report>().select("state").eq("company_id", company.getId()).eq("creator_id", creatorId).eq("create_date", createDate).last("limit 1"));
+                if (report.getState() == 1) {
+                    //最终审核通过了,需要更新加班餐补表的状态
+                    User creator = userMapper.selectById(creatorId);
+                    overtimeAllowanceMapper.update(new OvertimeAllowance().setReportPassed(true), new QueryWrapper<OvertimeAllowance>()
+                            .eq("corpwx_userid", creator.getCorpwxUserid()).eq("date", createDate));
+                }
             }
         }
         return httpRespMsg;
@@ -4241,6 +4251,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 pushReject(str, reporter, user.getName(), reason);
             }
         }
+        if (company.getId() == Constant.XI_HE_CHAO_DAO_COMPANY_ID && oneReport.getState() == 1) {
+            //撤回已通过的日报,需要撤回餐补加班记录的审核状态
+            overtimeAllowanceMapper.update(new OvertimeAllowance().setReportPassed(false), new QueryWrapper<OvertimeAllowance>().eq("corpwx_userid", corpwxUserid).eq("date", oneReport.getCreateDate()));
+        }
 
         if(timeType.getSyncSap()==1){
             List<Integer> reportIdList = ListUtil.convertIntegerIdsArrayToList(reportIds);
@@ -5385,6 +5399,14 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
                 for (Report r : distinctDayReport) {
                     hrAutoApprove(r.getCreatorId(), r.getCreateDate());
+                    //更新加班餐补表的日报状态
+                    Report report = reportMapper.selectOne(new QueryWrapper<Report>().select("state").eq("company_id", company.getId()).eq("creator_id", r.getCreatorId()).eq("create_date", r.getCreateDate()).last("limit 1"));
+                    if (report.getState() == 1) {
+                        //最终审核通过了,需要更新加班餐补表的状态
+                        User creator = userMapper.selectById(r.getCreatorId());
+                        overtimeAllowanceMapper.update(new OvertimeAllowance().setReportPassed(true), new QueryWrapper<OvertimeAllowance>()
+                                .eq("corpwx_userid", creator.getCorpwxUserid()).eq("date", r.getCreateDate()));
+                    }
                 }
 
             }
@@ -15900,7 +15922,9 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         if (departmentId != null) {
             allFirstLineDepartments = allFirstLineDepartments.stream().filter(dept -> dept.getDepartmentId().equals(departmentId)).collect(Collectors.toList());
             //要取子部门的所有集合
-            allFirstLineDepartments.addAll(getSubDepts(allFirstLineDepartments.get(0), allDeptList));
+            if (allFirstLineDepartments.size() > 0) {
+                allFirstLineDepartments.addAll(getSubDepts(allFirstLineDepartments.get(0), allDeptList));
+            }
         }
         if (allFirstLineDepartments.size() == 0) {
             return list;
@@ -15908,7 +15932,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         List<Integer> deptIds = allFirstLineDepartments.stream().map(Department::getDepartmentId).collect(Collectors.toList());
 
         // 1. 查询用户基础信息(只查一次)
-        List<HashMap> userList = reportMapper.getExtraWorkHoursList(year, companyId, deptIds, userId);
+        List<HashMap> userList = userCorpwxTimeMapper.getExtraWorkHoursList(year, companyId, deptIds, userId);
         if (userList.isEmpty()) {
             return list;
         }
@@ -15920,21 +15944,22 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 .filter(id -> id != null && !id.equals("null") && !id.isEmpty())
                 .distinct().collect(Collectors.toList());
 
-        // 3. 批量查询全年月度加班数据(只查一次report表)
-        List<Map<String, Object>> overtimeList = reportMapper.getMonthlyOvertimeByYear(year, companyId, userIds);
-        // 按userId分组: Map<userId, Map<month, overtime>>
+        // 3. 批量查询全年月度加班数据(只查一次user_corpwx_time表,使用otTime字段)
         Map<String, Map<String, Double>> overtimeMap = new HashMap<>();
-        for (Map<String, Object> row : overtimeList) {
-            String uid = String.valueOf(row.get("userId"));
-            String month = String.valueOf(row.get("month"));
-            double overtime = row.get("overtime") != null ? ((Number) row.get("overtime")).doubleValue() : 0.0;
-            overtimeMap.computeIfAbsent(uid, k -> new HashMap<>()).put(month, overtime);
+        if (!corpwxUserIds.isEmpty()) {
+            List<Map<String, Object>> overtimeList = userCorpwxTimeMapper.getMonthlyOvertimeByYear(year, companyId, corpwxUserIds);
+            for (Map<String, Object> row : overtimeList) {
+                String corpwxId = String.valueOf(row.get("corpwxUserid"));
+                String month = String.valueOf(row.get("month"));
+                double overtime = row.get("overtime") != null ? ((Number) row.get("overtime")).doubleValue() : 0.0;
+                overtimeMap.computeIfAbsent(corpwxId, k -> new HashMap<>()).put(month, overtime);
+            }
         }
 
         // 4. 批量查询全年月度调休数据(只查一次user_corpwx_time表)
         Map<String, Map<String, Double>> leaveMap = new HashMap<>();
         if (!corpwxUserIds.isEmpty()) {
-            List<Map<String, Object>> leaveList = reportMapper.getMonthlyLeaveByYear(year, companyId, corpwxUserIds);
+            List<Map<String, Object>> leaveList = userCorpwxTimeMapper.getMonthlyLeaveByYear(year, companyId, corpwxUserIds);
             for (Map<String, Object> row : leaveList) {
                 String corpwxId = String.valueOf(row.get("corpwxUserid"));
                 String month = String.valueOf(row.get("month"));
@@ -15945,9 +15970,9 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
         // 5. 在Java层拼装每个用户的月度数据
         for (HashMap userMap : userList) {
-            String uid = String.valueOf(userMap.get("id"));
             String corpwxId = userMap.get("corpwxUserid") != null ? String.valueOf(userMap.get("corpwxUserid")) : null;
-            Map<String, Double> userOvertimeByMonth = overtimeMap.getOrDefault(uid, new HashMap<>());
+            Map<String, Double> userOvertimeByMonth = (corpwxId != null && !corpwxId.equals("null")) ?
+                    overtimeMap.getOrDefault(corpwxId, new HashMap<>()) : new HashMap<>();
             Map<String, Double> userLeaveByMonth = (corpwxId != null && !corpwxId.equals("null")) ?
                     leaveMap.getOrDefault(corpwxId, new HashMap<>()) : new HashMap<>();
 

+ 67 - 11
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java

@@ -9,10 +9,7 @@ import com.management.platform.constant.Constant;
 import com.management.platform.controller.WeiXinCorpController;
 import com.management.platform.entity.*;
 import com.management.platform.mapper.*;
-import com.management.platform.service.OvertimeAllowanceService;
-import com.management.platform.service.TimeTypeService;
-import com.management.platform.service.WxCorpInfoService;
-import com.management.platform.service.WxCorpTemplateService;
+import com.management.platform.service.*;
 import com.management.platform.task.SFTPAsyncUploader;
 import com.management.platform.util.*;
 import lombok.extern.slf4j.Slf4j;
@@ -201,6 +198,10 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
     private com.management.platform.service.OvertimeService overtimeService;
     @Resource
     private ExceptionInfosMapper exceptionInfosMapper;
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Autowired
+    private UserCorpwxTimeService userCorpwxTimeService;
 
 
     //获取服务商provider_access_token
@@ -1172,21 +1173,21 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                         endCardTime.withHour(23).withMinute(59).withSecond(0).withNano(0);
                     }
                     if (corpInfo.getCompanyId() == Constant.HUA_YAN_COMPANY_ID) {
-                        double time = DateTimeUtil.calculateHuaYanWorkHours(minTime, maxTime, baseMorningStart, baseMorningEnd, baseAfternoonStart, baseAfternoonEnd, WorkDayCalculateUtils.isSundayOrHoliday(date));
+                        double time = DateTimeUtil.calculateHuaYanWorkHours(minTime, maxTime, WorkDayCalculateUtils.isSundayOrHoliday(date));
                         if (time < 0) {
                             time = 0;
                         }
                         userCorpwxTime.setWorkHours(time);
                     } else {
                         BigDecimal bigDecimal = new BigDecimal(Duration.between(startCardTime,endCardTime).toMinutes());
-//                     System.out.println("打卡时长(分钟):" + bigDecimal);
+//                        System.out.println("打卡时长(分钟):" + bigDecimal);
                         //开始时间在上午,结束时间在下午,要减去午休时长
                         if (startCardTime.compareTo(LocalTime.parse(baseMorningEnd,df)) <= 0 && endCardTime.compareTo(LocalTime.parse(baseAfternoonStart,df)) >= 0) {
                             //重新计算打卡工时时,需要减去中间午休时间
-                            bigDecimal = bigDecimal.divide(BigDecimal.valueOf(60),1,BigDecimal.ROUND_HALF_UP).subtract(new BigDecimal(restTime));
+                            bigDecimal = bigDecimal.divide(BigDecimal.valueOf(60),3,BigDecimal.ROUND_HALF_UP).subtract(new BigDecimal(restTime));
                             if (showLog) System.out.println("午休时间:"+restTime+", 减去后,工作时长="+bigDecimal);
                         } else {
-                            bigDecimal = bigDecimal.divide(BigDecimal.valueOf(60),1,BigDecimal.ROUND_HALF_UP);
+                            bigDecimal = bigDecimal.divide(BigDecimal.valueOf(60),3,BigDecimal.ROUND_HALF_UP);
                         }
                         double time = bigDecimal.doubleValue();
                         if (time < 0) {
@@ -1206,6 +1207,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                     if (userCorpwxTime.getCorpwxUserid().equals("woy9TkCAAACItPe1U_gkLcvivCMgpqRg") && date.isEqual(LocalDate.of(2024, 8, 28))) {
                         continue;
                     }
+
                     synchronized (userLock){
                         UserCorpwxTime item = userCorpwxTimeMapper.selectOne(new QueryWrapper<UserCorpwxTime>().eq("corpwx_userid", (String) objects[i])
                                 .eq("create_date", date));
@@ -1259,6 +1261,60 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 
     }
 
+    @Override
+    public Object calculateOvertime(Integer companyId, String ymonth) {
+        HttpRespMsg msg = new HttpRespMsg();
+        //计算企微考勤加班,只算非一线员工
+        List<Department> allDeptList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
+        List<Department> firstLineDepartments = allDeptList.stream().filter(Department::getFlag).collect(Collectors.toList());
+        List<Department> allFirstLineDepartments = new ArrayList<>(firstLineDepartments);
+        for (Department dept: firstLineDepartments) {
+            //取所有下级部门
+            allFirstLineDepartments.addAll(getSubDepts(dept, allDeptList));
+        }
+        List<Integer> deptIds = allFirstLineDepartments.stream().map(Department::getDepartmentId).collect(Collectors.toList());
+        if (!deptIds.isEmpty()) {
+            List<User> users = userMapper.selectList(new QueryWrapper<User>().in("department_id", deptIds).eq("company_id", companyId));
+            if (!users.isEmpty()) {
+                //获取到所有的考勤
+                DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+                LocalDate startDate = LocalDate.parse(ymonth + "-01", dateTimeFormatter);
+                LocalDate endDate = startDate.plusMonths(1).minusDays(1);
+                List<UserCorpwxTime> userCorpwxTimes = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>().eq("company_id", companyId).in("corpwx_userid", users.stream().map(User::getCorpwxUserid).collect(Collectors.toList()))
+                        .between("create_date", startDate, endDate));
+                //遍历,计算加班时长
+                List<UserCorpwxTime> updateList = new ArrayList<>();
+                for (UserCorpwxTime time : userCorpwxTimes) {
+                    double overtime = DateTimeUtil.calculateOvertime(time.getStartTime(), time.getEndTime());
+                    if (overtime > 0) {
+                        UserCorpwxTime copy = new UserCorpwxTime();
+                        copy.setId(time.getId());
+                        copy.setOtTime(overtime);
+                        copy.setOtStatus(1);
+                        updateList.add(copy);
+                    }
+                }
+                if (!updateList.isEmpty()) {
+                    userCorpwxTimeService.updateBatchById(updateList);
+                }
+            }
+
+        }
+        return msg;
+    }
+
+    private List<Department> getSubDepts(Department dp, List<Department> list) {
+        List<Department> collect = list.stream().filter(l -> dp.getDepartmentId().equals(l.getSuperiorId())).collect(Collectors.toList());;
+        List<Department> allList = new ArrayList<>();
+        allList.addAll(collect);
+        if (collect.size() > 0) {
+            collect.forEach(c->{
+                allList.addAll(getSubDepts(c, list));
+            });
+        }
+        return allList;
+    }
+
     private void handleAllowance(UserCorpwxTime userCorpwxTime, String baseMorningStart, String baseMorningEnd, String baseAfternoonStart, String baseAfternoonEnd, double restTime, double reportOvertime) {
         //注意,为了方便开发,对于表overtime-allowance的type类型增加-1值,表示只有考勤加班但是无补贴。
         OvertimeAllowance allowance = new OvertimeAllowance();
@@ -1475,7 +1531,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         String url = GET_CHECKIN_OPTION.replace("ACCESS_TOKEN", getCorpAccessToken(corpInfo));
         HttpHeaders headers = new HttpHeaders();
         headers.setContentType(MediaType.APPLICATION_JSON);
-        System.out.println("" + objects.toString());
+//        System.out.println("" + objects.toString());
         JSONObject reqParam = new JSONObject();
         reqParam.put("datetime", dataTime);
         reqParam.put("useridlist", objects);
@@ -1485,7 +1541,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 
         if (responseEntity.getStatusCode() == HttpStatus.OK) {
             String resp = responseEntity.getBody();
-            System.out.println("考勤规则:" + resp);
+//            System.out.println("考勤规则:" + resp);
             return resp;
         }
         return "";
@@ -1889,7 +1945,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 //                            DateTimeUtil.calculateWorkHours(ct.getStartTime(), ct.getEndTime());
                             if (corpInfo.getCompanyId() == Constant.HUA_YAN_COMPANY_ID) {
                                 //指定考勤规则
-                                timeDelta = DateTimeUtil.calculateHuaYanWorkHours(ct.getStartTime(), ct.getEndTime(), baseMorningStart, baseMorningEnd, baseAfternoonStart, baseAfternoonEnd, WorkDayCalculateUtils.isSundayOrHoliday(ct.getCreateDate()));
+                                timeDelta = DateTimeUtil.calculateHuaYanWorkHours(ct.getStartTime(), ct.getEndTime(), WorkDayCalculateUtils.isSundayOrHoliday(ct.getCreateDate()));
                             } else {
                                 timeDelta = DateTimeUtil.getHoursFromSeconds(DateTimeUtil.getSecondsFromTime(ct.getEndTime()) - DateTimeUtil.getSecondsFromTime(ct.getStartTime()));
                                 //超过下午上班的开始时间,需要减去午休的时间

+ 53 - 30
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/DateTimeUtil.java

@@ -132,21 +132,22 @@ public class DateTimeUtil {
      *
      * @param startTime              上班打卡时间,格式 "HH:mm",可带"次日"前缀
      * @param endTime                下班打卡时间,格式 "HH:mm",可带"次日"前缀
-     * @param baseMorningStartTime   基准上班时间,格式 "HH:mm"(如 "08:30")
-     * @param baseMorningEndTime     基准上午结束时间(保留兼容,暂未使用)
-     * @param baseAfternoonStartTime 基准下午开始时间(保留兼容,暂未使用)
-     * @param baseAfternoonEndTime   基准下班时间,格式 "HH:mm"(如 "17:30")
      * @param isHolidayOrSunday      是否为周日或法定假日(true则不扣就餐时间)
      * @return 计算后的工时(小时,最小单位0.5)
      */
-    public static double calculateHuaYanWorkHours(String startTime, String endTime, String baseMorningStartTime,
-                                                  String baseMorningEndTime, String baseAfternoonStartTime, String baseAfternoonEndTime, boolean isHolidayOrSunday) {
+    public static double calculateHuaYanWorkHours(String startTime, String endTime, boolean isHolidayOrSunday) {
+        String baseMorningStartTime = "08:30";
+        String baseAfternoonEndTime = "17:30";
         // 处理"次日"前缀
         boolean isEndNextDay = endTime.startsWith("次日");
         if (isEndNextDay) {
             endTime = endTime.substring(2).trim();
         }
 
+        if (startTime.compareTo("08:30") > 0 && startTime.compareTo("08:35") <= 0) {
+            startTime = baseMorningStartTime;//迟到5分钟不算迟到,按8:30计算。
+        }
+
         // 解析打卡时间为分钟数(从0点起)
         String[] startParts = startTime.split(":");
         String[] endParts = endTime.split(":");
@@ -352,34 +353,34 @@ public class DateTimeUtil {
 
         // 题目示例:8:17早到,早来13min→floor(13/30)*30=0min;下班17:35→超5min→17:30;
         // 正常工时=floor((1050-510)/30)*30=540min;合计=0+540=540min;扣午餐60→480min=8.0h
-        System.out.println("8:17~17:35 (周一): " + calculateHuaYanWorkHours("08:17", "17:35", "08:30", "12:00", "13:00", "17:30", false));
+        System.out.println("8:22~19:40 (周一): " + calculateHuaYanWorkHours("08:22", "19:40", false));
         // 期望: 8.0
 
         // 8:45不早到,下班18:05→超35min→18:00(1080);工时=floor((1080-525)/30)*30=floor(555/30)*30=540min
         // 扣午餐60+扣晚餐30→450min=7.5h
-        System.out.println("8:45~18:05 (周一): " + calculateHuaYanWorkHours("08:45", "18:05", "08:30", "12:00", "13:00", "17:30", false));
-        // 期望: 7.5
-
-        // 9:05不早到,下班18:35→超65min→18:30(1110);工时=floor((1110-545)/30)*30=floor(565/30)*30=540min
-        // 扣午餐60+扣晚餐30→450min=7.5h
-        System.out.println("9:05~18:35 (周一): " + calculateHuaYanWorkHours("09:05", "18:35", "08:30", "12:00", "13:00", "17:30", false));
-        // 期望: 7.5
-
-        // 7:50早到,早来40min→floor(40/30)*30=30min;下班17:30→正常工时=floor(540/30)*30=540min;合计=570min
-        // 扣午餐60→510min=8.5h
-        System.out.println("7:50~17:30 (周一): " + calculateHuaYanWorkHours("07:50", "17:30", "08:30", "12:00", "13:00", "17:30", false));
-        // 期望: 8.5
-
-        // 8:30~17:30 (周日/假日): 不扣就餐,工时=floor(540/30)*30=540min=9.0h
-        System.out.println("8:30~17:30 (周日): " + calculateHuaYanWorkHours("08:30", "17:30", "08:30", "12:00", "13:00", "17:30", true));
-        // 期望: 9.0
-
-        // 8:30~17:30 (周一): 工时=540min,扣午餐60→480min=8.0h
-        System.out.println("8:30~17:30 (周一): " + calculateHuaYanWorkHours("08:30", "17:30", "08:30", "12:00", "13:00", "17:30", false));
-        // 期望: 8.0
-
-        // 8:30~18:30 (周一): 下班超60min→18:30(1110);工时=floor(600/30)*30=600min;扣午餐60+晚餐30→510min=8.5h
-        System.out.println("8:30~18:30 (周一): " + calculateHuaYanWorkHours("08:30", "18:30", "08:30", "12:00", "13:00", "17:30", false));
+//        System.out.println("8:45~18:05 (周一): " + calculateHuaYanWorkHours("08:45", "18:05", "08:30", "12:00", "13:00", "17:30", false));
+//        // 期望: 7.5
+//
+//        // 9:05不早到,下班18:35→超65min→18:30(1110);工时=floor((1110-545)/30)*30=floor(565/30)*30=540min
+//        // 扣午餐60+扣晚餐30→450min=7.5h
+//        System.out.println("9:05~18:35 (周一): " + calculateHuaYanWorkHours("09:05", "18:35", "08:30", "12:00", "13:00", "17:30", false));
+//        // 期望: 7.5
+//
+//        // 7:50早到,早来40min→floor(40/30)*30=30min;下班17:30→正常工时=floor(540/30)*30=540min;合计=570min
+//        // 扣午餐60→510min=8.5h
+//        System.out.println("7:50~17:30 (周一): " + calculateHuaYanWorkHours("07:50", "17:30", "08:30", "12:00", "13:00", "17:30", false));
+//        // 期望: 8.5
+//
+//        // 8:30~17:30 (周日/假日): 不扣就餐,工时=floor(540/30)*30=540min=9.0h
+//        System.out.println("8:30~17:30 (周日): " + calculateHuaYanWorkHours("08:30", "17:30", "08:30", "12:00", "13:00", "17:30", true));
+//        // 期望: 9.0
+//
+//        // 8:30~17:30 (周一): 工时=540min,扣午餐60→480min=8.0h
+//        System.out.println("8:30~17:30 (周一): " + calculateHuaYanWorkHours("08:30", "17:30", "08:30", "12:00", "13:00", "17:30", false));
+//        // 期望: 8.0
+//
+//        // 8:30~18:30 (周一): 下班超60min→18:30(1110);工时=floor(600/30)*30=600min;扣午餐60+晚餐30→510min=8.5h
+//        System.out.println("8:30~18:30 (周一): " + calculateHuaYanWorkHours("08:30", "18:30", "08:30", "12:00", "13:00", "17:30", false));
         // 期望: 8.5
     }
 
@@ -389,4 +390,26 @@ public class DateTimeUtil {
         LocalDate lastDay = localDate.withDayOfMonth(localDate.getMonth().length(localDate.isLeapYear()));
         return dateTimeFormatter.format(lastDay);
     }
+
+    public static double calculateOvertime(String startTime, String endTime) {
+        String workEndTime = "17:30";
+        if (StringUtils.isEmpty(startTime) || StringUtils.isEmpty(endTime) || endTime.compareTo(workEndTime) < 0) {
+            return 0;
+        }
+        // 计算加班
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm");
+        String startDate = "1990-01-01";
+        String endDate = "1990-01-01";
+        if (startTime.compareTo(endTime) > 0 || endTime.startsWith("次日")) {
+            endDate = "1990-01-02";
+            if (endTime.startsWith("次日")) {
+                endTime = endTime.substring(2).trim();
+            }
+        }
+        LocalDateTime start = LocalDateTime.parse(startDate + " " + workEndTime, dateTimeFormatter);
+        LocalDateTime end = LocalDateTime.parse(endDate + " " + endTime, dateTimeFormatter);
+        Duration duration = Duration.between(start, end);
+        double overtime = duration.toMinutes() / 60.0;
+        return getHalfHoursFromDouble(overtime);
+    }
 }

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/OvertimeAllowanceMapper.xml

@@ -42,7 +42,7 @@
 
     <!-- 按人员合计统计餐补数据的公共 WHERE 条件 -->
     <sql id="Summary_Where">
-        WHERE company_id = #{companyId}
+        WHERE company_id = #{companyId} and report_passed = true
         <choose>
             <when test="isAllowance == true">
                 AND type &gt;= 0

+ 1 - 40
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml

@@ -2069,46 +2069,7 @@
         ORDER BY a.creator_id ASC
     </select>
 
-    <!--获取额外加班工时和调休工时表 - 用户基础信息查询 -->
-    <select id="getExtraWorkHoursList" resultType="java.util.Map">
-        select user.id, user.name, user.`department_id` as departmentId, department.`department_name` as departmentName,
-               user.`corpwx_userid` as corpwxUserid
-        from `user` left join department on department.`department_id` = user.`department_id`
-        where user.`company_id` = #{companyId}
-        <if test="userId != null and userId != ''">
-            and user.id = #{userId}
-        </if>
-        and user.department_id in
-        <foreach item="item" collection="deptIds" separator="," open="(" close=")" index="">
-            #{item, jdbcType=INTEGER}
-        </foreach>
-    </select>
 
-    <!--获取指定年度的月度加班工时汇总(按人员和月份分组)-->
-    <select id="getMonthlyOvertimeByYear" resultType="java.util.Map">
-        select creator_id as userId, DATE_FORMAT(create_date,'%m') as month, sum(overtime_hours) as overtime
-        from report
-        where company_id = #{companyId}
-          and DATE_FORMAT(create_date,'%Y') = #{year}
-          and creator_id in
-        <foreach item="item" collection="userIds" separator="," open="(" close=")" index="">
-            #{item}
-        </foreach>
-        group by creator_id, DATE_FORMAT(create_date,'%m')
-    </select>
-
-    <!--获取指定年度的月度调休工时汇总(按企微用户ID和月份分组)-->
-    <select id="getMonthlyLeaveByYear" resultType="java.util.Map">
-        select corpwx_userid as corpwxUserid, DATE_FORMAT(create_date,'%m') as month, sum(ask_leave_time) as leavetime
-        from user_corpwx_time
-        where company_id = #{companyId}
-          and ask_leave_type = 'T'
-          and DATE_FORMAT(create_date,'%Y') = #{year}
-          and corpwx_userid in
-        <foreach item="item" collection="corpwxUserIds" separator="," open="(" close=")" index="">
-            #{item}
-        </foreach>
-        group by corpwx_userid, DATE_FORMAT(create_date,'%m')
-    </select>
+
 
 </mapper>

+ 42 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserCorpwxTimeMapper.xml

@@ -88,4 +88,46 @@
             AND create_date &lt;= #{endDate}
         </if>
     </select>
+
+    <!--获取指定年度的月度加班工时汇总(按人员和月份分组)-->
+    <select id="getMonthlyOvertimeByYear" resultType="java.util.Map">
+        select corpwx_userid as corpwxUserid, DATE_FORMAT(create_date,'%m') as month, sum(ot_time) as overtime
+        from user_corpwx_time
+        where company_id = #{companyId}
+        and DATE_FORMAT(create_date,'%Y') = #{year}
+        and corpwx_userid in
+        <foreach item="item" collection="corpwxUserIds" separator="," open="(" close=")" index="">
+            #{item}
+        </foreach>
+        group by corpwx_userid, DATE_FORMAT(create_date,'%m')
+    </select>
+
+    <!--获取指定年度的月度调休工时汇总(按企微用户ID和月份分组)-->
+    <select id="getMonthlyLeaveByYear" resultType="java.util.Map">
+        select corpwx_userid as corpwxUserid, DATE_FORMAT(create_date,'%m') as month, sum(ask_leave_time) as leavetime
+        from user_corpwx_time
+        where company_id = #{companyId}
+        and ask_leave_type = 'T'
+        and DATE_FORMAT(create_date,'%Y') = #{year}
+        and corpwx_userid in
+        <foreach item="item" collection="corpwxUserIds" separator="," open="(" close=")" index="">
+            #{item}
+        </foreach>
+        group by corpwx_userid, DATE_FORMAT(create_date,'%m')
+    </select>
+
+    <!--获取额外加班工时和调休工时表 - 用户基础信息查询 -->
+    <select id="getExtraWorkHoursList" resultType="java.util.Map">
+        select user.id, user.name, user.`department_id` as departmentId, department.`department_name` as departmentName,
+        user.`corpwx_userid` as corpwxUserid
+        from `user` left join department on department.`department_id` = user.`department_id`
+        where user.`company_id` = #{companyId}
+        <if test="userId != null and userId != ''">
+            and user.id = #{userId}
+        </if>
+        and user.department_id in
+        <foreach item="item" collection="deptIds" separator="," open="(" close=")" index="">
+            #{item, jdbcType=INTEGER}
+        </foreach>
+    </select>
 </mapper>

+ 72 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -1102,11 +1102,18 @@
           <!-- <p :style="ins == 9 ? 'float: right;margin-right: 25px;width:20%' : 'float: right;margin-right: 25px;width:10%'" > -->
           <p
             :style="`${
-              ins == 9 || ins == 14 || ins == 21 ? 'width: 20%' : 'width: 10%'
+              ins == 9 || ins == 14 || ins == 21 || ins == 45 ? 'width: 20%' : 'width: 10%'
             }`"
             class="tableRightBtn"
             v-if="ins != 31"
           >
+            <el-button
+              type="primary"
+              @click="openCalculateOvertimeDialog"
+              size="mini"
+              v-if="ins == 45"
+              >计算加班时长</el-button
+            >
             <el-button
               type="primary"
               :loading="exportReportLoading"
@@ -7897,6 +7904,35 @@
         <el-button @click="attachmentDialogVisible = false">关闭</el-button>
       </span>
     </el-dialog>
+
+    <!-- 计算加班时长弹窗 -->
+    <el-dialog
+      title="计算加班时长"
+      :visible.sync="calculateOvertimeDialogVisible"
+      width="400px"
+      :before-close="handleClose"
+    >
+      <el-form label-width="80px">
+        <el-form-item label="选择年月">
+          <el-date-picker
+            v-model="calculateOvertimeYmonth"
+            type="month"
+            placeholder="请选择年月"
+            value-format="yyyy-MM"
+            :clearable="false"
+            style="width: 220px"
+          ></el-date-picker>
+        </el-form-item>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="calculateOvertimeDialogVisible = false">取消</el-button>
+        <el-button
+          type="primary"
+          :loading="calculateOvertimeLoading"
+          @click="doCalculateOvertime"
+        >开始计算</el-button>
+      </span>
+    </el-dialog>
   </section>
 </template>
 
@@ -8405,6 +8441,11 @@ export default {
       extraWorkHoursLoading: false,
       extraWorkHoursYear: new Date().getFullYear().toString(),
 
+      // 计算加班时长弹窗
+      calculateOvertimeDialogVisible: false,
+      calculateOvertimeYmonth: new Date().getFullYear() + '-' + String(new Date().getMonth() + 1).padStart(2, '0'),
+      calculateOvertimeLoading: false,
+
       // 考勤异常表
       exceptionReportList: [],
       exceptionReportLoading: false,
@@ -12953,6 +12994,36 @@ export default {
       }
       return "";
     },
+
+    // 打开计算加班时长弹窗
+    openCalculateOvertimeDialog() {
+      const now = new Date();
+      const year = now.getFullYear();
+      const month = String(now.getMonth() + 1).padStart(2, '0');
+      this.calculateOvertimeYmonth = year + '-' + month;
+      this.calculateOvertimeDialogVisible = true;
+    },
+
+    // 执行计算加班时长
+    doCalculateOvertime() {
+      if (!this.calculateOvertimeYmonth) {
+        this.$message({ message: '请选择年月', type: 'warning' });
+        return;
+      }
+      this.calculateOvertimeLoading = true;
+      this.postData('/wx-corp-info/calculateOvertime', {
+        companyId: this.user.companyId,
+        ymonth: this.calculateOvertimeYmonth,
+      })
+        .then(() => {
+          this.$message({ message: '计算成功', type: 'success' });
+          this.calculateOvertimeDialogVisible = false;
+          this.getExtraWorkHours();
+        })
+        .finally(() => {
+          this.calculateOvertimeLoading = false;
+        });
+    },
   },
 };
 </script>

+ 5 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -639,7 +639,10 @@
                                 <!--子项目 -->
                                 <el-select v-model="domain.subProjectId" :placeholder="$t('defaultText.PleaseSelectaSub-item')" style="width:200px;margin-left:10px;" clearable="true"
                                 :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)" @change="updateSubProject">
-                                    <el-option v-for="item in domain.subProjectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                                    <el-option v-for="item in domain.subProjectList" :key="item.id" :label="item.name + '\u3000' + (item.code||'')" :value="item.id">
+                                        <span style="float: left; color: #8492a6; font-size: 13px;">{{ item.code }}</span>
+                                        <span style="float: right;">{{ item.name }}</span>
+                                    </el-option>
                                 </el-select>
                             </template>
                             <template v-if="user.companyId == yuzhongCompId">
@@ -7390,6 +7393,7 @@
             },
             // 获取个人某天的日报 111111
             getReport() {
+                this.workForm.time = null;
                 this.http.post( this.port.report.getPort, {
                     date: this.workForm.createDate
                 },