Browse Source

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

cs 2 years ago
parent
commit
a435f5e94f
29 changed files with 732 additions and 55 deletions
  1. 2 2
      fhKeeper/formulahousekeeper/inva_4_tivo/index.html
  2. 55 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/Config.java
  3. 260 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  4. 52 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/SubProjectController.java
  5. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/SubProject.java
  6. 59 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java
  7. 10 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  8. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  9. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  10. 7 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  11. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  12. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  13. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/SubProjectMapper.xml
  14. 2 0
      fhKeeper/formulahousekeeper/octopus/src/views/Home.vue
  15. 2 0
      fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue
  16. 45 4
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CommonUploadController.java
  17. 3 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CompanyController.java
  18. 3 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/entity/Company.java
  19. 3 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/resources/application.yml
  20. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue
  21. 4 4
      fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue
  22. 100 4
      fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue
  23. 14 11
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  24. BIN
      fhKeeper/formulahousekeeper/timesheet_h5/src/assets/img/chong.png
  25. 7 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/router/index.js
  26. 3 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue
  27. 1 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/login/index.vue
  28. 1 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/index.vue
  29. 78 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/test/clearStorage.vue

+ 2 - 2
fhKeeper/formulahousekeeper/inva_4_tivo/index.html

@@ -16,7 +16,7 @@
     <script src="js/jquery1.42.min.js"></script>
     <script src="js/jquery.SuperSlide.2.1.3.js"></script>
     <!-- 统计文件 -->
-    <!-- <script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
+    <script charset="UTF-8" id="LA_COLLECT" src="//sdk.51.la/js-sdk-pro.min.js"></script>
     <script>LA.init({id: "Jk62Sh8gvUhl1xcU",ck: "Jk62Sh8gvUhl1xcU"})</script>
     <script>
         let str = (navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i)) ? 'app': 'pc';
@@ -28,7 +28,7 @@
             let url = urlArr[0] + 'ttkuaiban.com/homemovement.html'
             window.location.replace(url)
         }
-    </script> -->
+    </script>
     <script>
         function bodyScale() {
             var devicewidth = document.documentElement.clientWidth;

+ 55 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/Config.java

@@ -1,14 +1,67 @@
 package com.management.platform.config;
 
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.socket.PlainConnectionSocketFactory;
+import org.apache.http.conn.ssl.NoopHostnameVerifier;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.tomcat.jni.SSL;
 import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
 import org.springframework.web.client.RestTemplate;
 
+import javax.net.ssl.SSLContext;
+import java.security.KeyManagementException;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+
 @Configuration
 public class Config {
+
   @Bean
-  public RestTemplate restTemplate(RestTemplateBuilder builder){
-    return builder.build();
+  public RestTemplate restTemplate()
+  {
+    ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
+    System.out.println("@@@===初始化RestRemplate===@@");
+    return new RestTemplate(requestFactory);
+  }
+  /**
+   * Apache HttpClient
+   *
+   * @return
+   * @see [类、类#方法、类#成员]
+   */
+  private HttpClient httpClient()
+  {
+    // 支持HTTP、HTTPS
+    Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
+            .register("http", PlainConnectionSocketFactory.getSocketFactory())
+            .register("https", SSLConnectionSocketFactory.getSocketFactory())
+            .build();
+    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
+    connectionManager.setMaxTotal(200);
+    connectionManager.setDefaultMaxPerRoute(100);
+    connectionManager.setValidateAfterInactivity(2000);
+    RequestConfig requestConfig = RequestConfig.custom()
+            // 服务器返回数据(response)的时间,超时抛出read timeout
+            .setSocketTimeout(65000)
+            // 连接上服务器(握手成功)的时间,超时抛出connect timeout
+            .setConnectTimeout(5000)
+            // 从连接池中获取连接的超时时间,超时抛出ConnectionPoolTimeoutException
+            .setConnectionRequestTimeout(1000)
+            .build();
+    return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
   }
 }

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

@@ -12,9 +12,11 @@ import com.management.platform.entity.vo.SysRichFunction;
 import com.management.platform.entity.vo.WorktimeItem;
 import com.management.platform.mapper.*;
 import com.management.platform.service.*;
-import com.management.platform.util.HttpRespMsg;
-import com.management.platform.util.MessageUtils;
-import com.management.platform.util.WorkDayCalculateUtils;
+import com.management.platform.util.*;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.WebSocket;
+import org.apache.ibatis.annotations.Param;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.*;
 import org.springframework.util.StringUtils;
@@ -24,14 +26,17 @@ import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.io.File;
 import java.io.IOException;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.DecimalFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
@@ -86,6 +91,10 @@ public class ReportController {
     private UserCorpwxTimeMapper userCorpwxTimeMapper;
     @Resource
     private UserMapper userMapper;
+    @Resource
+    private UserFvTimeService userFvTimeService;
+    @Resource
+    private LeaveSheetService leaveSheetService;
 
     //获取任务相关的日报列表
     @RequestMapping("/getTaskReportList")
@@ -1703,5 +1712,253 @@ public class ReportController {
         return reportService.correctWorkingTime(userIds,startDate,endDate);
     }
 
+    @RequestMapping("/fvTongBu")
+    public void fvTongBu(){
+        List<TimeType> timeTypeList = timeTypeMapper.selectList(new QueryWrapper<TimeType>().eq("sync_fanwei", 1));
+        List<Integer> compIds = timeTypeList.stream().map(TimeType::getCompanyId).collect(Collectors.toList());
+        if(compIds.isEmpty()){
+            return;
+        }
+        for (Integer compId : compIds) {
+            DockWithMLD dockWithMLD=new DockWithMLD();
+            JSONObject jsonObject=new JSONObject();
+            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+            DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("HH:mm");
+            //需要修改************************************************  日期
+            LocalDateTime yesterday = LocalDateTime.now().minusDays(2);
+            String startDate = dtf.format(yesterday);
+            String endDate = dtf.format(yesterday.plusDays(30));
+            List<UserFvTime> oldUserFvTimeList = userFvTimeService.list(new QueryWrapper<UserFvTime>().eq("company_id", compId));
+            List<LeaveSheet> oldLeaveSheetList = leaveSheetService.list(new QueryWrapper<LeaveSheet>().eq("company_id", compId));
+            List<BusinessTrip> oldBusinessTripList = businessTripService.list(new QueryWrapper<BusinessTrip>().eq("company_id", compId));
+            /*jsonObject.put("startDate",startDate);
+            jsonObject.put("endDate",endDate);*/
+            jsonObject.put("workDate",startDate);
+            jsonObject.put("gmtFinished",startDate);
+            String jsonString = jsonObject.toJSONString();
+            List<UserFvTime> userFvTimeList=new ArrayList<>();
+            List<LeaveSheet> leaveSheetList=new ArrayList<>();
+            List<BusinessTrip> businessTripList=new ArrayList<>();
+            TimeType allDay = timeTypeMapper.selectOne(new QueryWrapper<TimeType>().eq("company_id", compId));
+            List<User> allUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", compId));
+            //获取休息设置
+            TimeAutoExclude timeAutoExclude = timeAutoExcludeMapper.selectOne(new QueryWrapper<TimeAutoExclude>().eq("company_id", compId));
+            //Todo: 获取考勤打卡数据
+            HttpRespMsg workDataMsg = dockWithMLD.getResult("http://10.1.10.51:20175/api/cube/restful/interface/getModeDataPageList/getWorkData", jsonString);
+            List<Map<String,Object>> workDataList= (List<Map<String, Object>>) workDataMsg.data;
+            //Todo: 获取外出打卡数据
+            HttpRespMsg outWorkDataMsg = dockWithMLD.getResult("http://10.1.10.51:20175/api/cube/restful/interface/getModeDataPageList/getOutRecord", jsonString);
+            List<Map<String,Object>> outWorkDataList= (List<Map<String, Object>>) outWorkDataMsg.data;
+            //Todo: 获取考勤补卡数据
+            HttpRespMsg replaceWorkDataMsg = dockWithMLD.getResult("http://10.1.10.51:20175/api/cube/restful/interface/getModeDataPageList/getReplaceRecord", jsonString);
+            List<Map<String,Object>> replaceWorkDataList= (List<Map<String, Object>>) replaceWorkDataMsg.data;
+            List<String> userIds = workDataList.stream().map(map -> String.valueOf(map.get("userId"))).distinct().collect(Collectors.toList());
+            List<User> userList = userMapper.selectList(new QueryWrapper<User>().in("job_number", userIds));
+            List<LocalDateTime> dateTimeList = getDays(yesterday, yesterday.plusDays(2));
+            for (User user : userList) {
+                for (LocalDateTime localDateTime : dateTimeList) {
+                    LocalDate workDate=localDateTime.toLocalDate();
+                    //当天的考勤记录
+                    List<Map<String, Object>> list = workDataList.stream().filter(wl -> wl.get("userId").equals(user.getJobNumber())&&wl.get("workDate").equals(workDate.format(dtf))).collect(Collectors.toList());
+                    UserFvTime userFvTime=new UserFvTime();
+                    userFvTime.setWorkDate(workDate);
+                    LocalTime startTime=null;
+                    LocalTime endTime=null;
+                    for (Map<String, Object> map : list) {
+                        if(String.valueOf(map.get("signtype")).equals("签到")&&!map.get("signtime").equals("")){
+                            startTime=LocalTime.parse(String.valueOf(map.get("signtime")), dtf2);
+                        }
+                        if(String.valueOf(map.get("signtype")).equals("签退")&&!map.get("signtime").equals("")){
+                            endTime=LocalTime.parse(String.valueOf(map.get("signtime")), dtf2);
+                        }
+                        //处理外出记录 有外出记录默认以默认规则作为考勤数据来源
+                        Optional<Map<String, Object>> outFirst = outWorkDataList.stream().filter(ol -> ol.get("ycbgkssj").equals(map.get("workDate")) && ol.get("rybh").equals(map.get("userId"))).findFirst();
+                        if(outFirst.isPresent()){
+                            if(startTime==null||LocalTime.parse(String.valueOf(outFirst.get().get("kssj")), dtf2).isBefore(startTime)){
+                                startTime=LocalTime.parse(String.valueOf(outFirst.get().get("kssj")), dtf2);
+                            }
+                            if(endTime==null||LocalTime.parse(String.valueOf(outFirst.get().get("jssj")), dtf2).isBefore(endTime)){
+                                endTime=LocalTime.parse(String.valueOf(outFirst.get().get("jssj")), dtf2);
+                            }
+                        }
+                        //处理补卡记录 有补卡记录以补卡记录作为考勤数据来源
+                        Optional<Map<String, Object>> replaceFirst = replaceWorkDataList.stream().filter(rl -> rl.get("kqrq").equals(map.get("workDate")) && rl.get("rybh").equals(map.get("userId"))).findFirst();
+                        if(replaceFirst.isPresent()){
+                            if(startTime==null||replaceFirst.get().get("bklx").equals("上班")){
+                                startTime=LocalTime.parse(String.valueOf(replaceFirst.get().get("bksj")), dtf2);
+                            }
+                            if(endTime==null||replaceFirst.get().get("bklx").equals("下班")){
+                                endTime=LocalTime.parse(String.valueOf(replaceFirst.get().get("bksj")), dtf2);
+                            }
+                        }
+                    }
+                    if(compIds.contains(user.getCompanyId())){
+                        if(startTime==null||endTime==null){
+                            continue;
+                        }
+                        Duration between = Duration.between(startTime, endTime);
+                        userFvTime.setStartTime(startTime.format(dtf2));
+                        userFvTime.setEndTime(endTime.format(dtf2));
+                        userFvTime.setCompanyId(user.getCompanyId());
+                        userFvTime.setUserId(user.getId());
+                        long workHours = between.toHours();
+                        long restHours;
+                        if(startTime.isBefore(LocalTime.parse(timeAutoExclude.getStartTime(),dtf2))
+                                &&endTime.isAfter(LocalTime.parse(timeAutoExclude.getEndTime(),dtf2))){
+                            Duration bt = Duration.between(LocalTime.parse(timeAutoExclude.getStartTime(),dtf2), LocalTime.parse(timeAutoExclude.getEndTime(),dtf2));
+                            restHours=bt.toHours();
+                        }else {
+                            restHours=0;
+                        }
+                        userFvTime.setWorkHours(BigDecimal.valueOf(workHours).subtract(BigDecimal.valueOf(restHours)).floatValue());
+                        Optional<UserFvTime> first = oldUserFvTimeList.stream().filter(ol -> ol.getWorkDate().isEqual(userFvTime.getWorkDate()) && ol.getUserId().equals(userFvTime.getUserId())).findFirst();
+                        if(first.isPresent()){
+                            userFvTime.setId(first.get().getId());
+                        }
+                        if(endTime.isBefore(startTime)){
+                            continue;
+                        }
+                        userFvTimeList.add(userFvTime);
+                    }
+                }
+            }
+            if(userFvTimeList.size()>0){
+                userFvTimeService.saveOrUpdateBatch(userFvTimeList);
+            }
+            System.out.println(userFvTimeList);
+            //Todo: 获取请假数据
+            HttpRespMsg leaveRecordMsg = dockWithMLD.getResult("http://10.1.10.51:20175/api/cube/restful/interface/getModeDataPageList/getLeaveRecord", jsonString);
+            List<Map<String,Object>> leaveRecordList= (List<Map<String, Object>>) leaveRecordMsg.data;
+            for (Map<String, Object> map : leaveRecordList) {
+                Optional<User> optional=allUserList.stream().filter(al->al.getJobNumber()!=null&&al.getJobNumber().equals(map.get("rybh"))).findFirst();
+                if(!optional.isPresent()){
+                    continue;
+                }
+                User user=optional.get();
+                /*System.out.println("泛微同步人员请假数据----"+user.getName());*/
+                if(compIds.contains(user.getCompanyId())){
+                    LeaveSheet leaveSheet=new LeaveSheet();
+                    leaveSheet.setCompanyId(user.getCompanyId());
+                    leaveSheet.setStatus(0);
+                    leaveSheet.setOwnerId(user.getId());
+                    leaveSheet.setOwnerName(user.getName());
+                    leaveSheet.setStartDate(LocalDate.parse(String.valueOf(map.get("startDate")),dtf1));
+                    leaveSheet.setEndDate(LocalDate.parse(String.valueOf(map.get("endDate")),dtf1));
+                    Integer timeType=null;
+                    switch (String.valueOf(map.get("timeType"))){
+                        case "小时":timeType=1;
+                            break;
+                        case "天":timeType=0;
+                            break;
+                    }
+                    leaveSheet.setTimeType(timeType);
+                    leaveSheet.setTimeDays(Float.parseFloat(((String) map.get("timeDays")).equals("")?"0.00":((String) map.get("timeDays"))));
+                    if (timeType == 0){
+                        leaveSheet.setTimeHours(leaveSheet.getTimeDays()*allDay.getAllday());
+                    }else {
+                        leaveSheet.setTimeHours(Float.parseFloat(((String) map.get("timeHours")).equals("")?"0.00":((String) map.get("timeHours"))));
+                    }
+                    Integer leaveType;
+                    switch (String.valueOf(map.get("leaveType"))){
+                        case "事假":leaveType=0;
+                            break;
+                        case "病假":leaveType=1;
+                            break;
+                        case "年假":leaveType=2;
+                            break;
+                        case "产假":leaveType=3;
+                            break;
+                        case "婚假":leaveType=4;
+                            break;
+                        case "丧假":leaveType=5;
+                            break;
+                        case "调休":leaveType=6;
+                            break;
+                        case "陪产假":leaveType=7;
+                            break;
+                        default:leaveType=8;
+                    }
+                    leaveSheet.setLeaveType(leaveType);
+                    leaveSheet.setProcinstId(String.valueOf(map.get("id")));
+                    leaveSheet.setGmtFinished(String.valueOf(map.get("gmtFinished")));
+                    Optional<LeaveSheet> first = oldLeaveSheetList.stream().filter(ol -> ol.getStartDate().isEqual(leaveSheet.getStartDate())&&ol.getEndDate().isEqual(leaveSheet.getEndDate())&& ol.getOwnerId().equals(leaveSheet.getOwnerId())&&(ol.getProcinstId()!=null&& ol.getProcinstId().equals(leaveSheet.getProcinstId()))).findFirst();
+                    if(first.isPresent()){
+                        leaveSheet.setId(first.get().getId());
+                    }
+                    leaveSheetList.add(leaveSheet);
+                }
+            }
+            if(leaveSheetList.size()>0){
+                leaveSheetService.saveOrUpdateBatch(leaveSheetList);
+            }
+            System.out.println(leaveRecordList);
+            //Todo: 获取出差数据
+            HttpRespMsg travelRecordMsg = dockWithMLD.getResult("http://10.1.10.51:20175/api/cube/restful/interface/getModeDataPageList/getTravelRecord", jsonString);
+            List<Map<String,Object>> travelRecordList= (List<Map<String, Object>>) travelRecordMsg.data;
+            for (Map<String, Object> map : travelRecordList) {
+                Optional<User> optional=allUserList.stream().filter(al->al.getJobNumber()!=null&&al.getJobNumber().equals(map.get("rybh"))).findFirst();
+                if(!optional.isPresent()){
+                    continue;
+                }
+                User user=optional.get();
+                if(compIds.contains(user.getCompanyId())){
+                    BusinessTrip businessTrip=new BusinessTrip();
+                    businessTrip.setCompanyId(user.getCompanyId());
+                    businessTrip.setOwnerId(user.getId());
+                    businessTrip.setOwnerName(user.getName());
+                    businessTrip.setStartDate(LocalDate.parse(String.valueOf(map.get("startDate")),dtf1));
+                    businessTrip.setEndDate(LocalDate.parse(String.valueOf(map.get("endDate")),dtf1));
+                    Integer way=null;
+                    switch (String.valueOf(map.get("way"))){
+                        case "飞机":way=0;
+                            break;
+                        case "高铁/火车":way=1;
+                            break;
+                        case "汽车":way=2;
+                            break;
+                        case "轮船":way=3;
+                            break;
+                        case "其他":way=4;
+                            break;
+                    }
+                    businessTrip.setWay(way);
+                    businessTrip.setCityFrom((String) map.get("cityFrom"));
+                    businessTrip.setCityTo((String) map.get("cityTo"));
+                    Integer goBack=null;
+                    switch (String.valueOf(map.get("goBack"))){
+                        case "单程":goBack=0;
+                            break;
+                        case "往返":goBack=1;
+                            break;
+                    }
+                    businessTrip.setGoBack(goBack);
+                    Double dayCount =Double.valueOf(String.valueOf(map.get("dayCount")));
+                    businessTrip.setDayCount(dayCount.intValue());
+                    businessTrip.setProcinstId((String) map.get("id"));
+                    businessTrip.setGmtFinished((String)map.get("gmtFinished"));
+                    Optional<BusinessTrip> first = oldBusinessTripList.stream().filter(ol -> ol.getStartDate().isEqual(businessTrip.getStartDate())&&ol.getEndDate().isEqual(businessTrip.getEndDate())&& ol.getOwnerId().equals(businessTrip.getOwnerId())&&(ol.getProcinstId()!=null&&ol.getProcinstId().equals(businessTrip.getProcinstId()))).findFirst();
+                    if(first.isPresent()){
+                        businessTrip.setId(first.get().getId());
+                    }
+                    businessTripList.add(businessTrip);
+                }
+            }
+            if(businessTripList.size()>0){
+                businessTripService.saveOrUpdateBatch(businessTripList);
+            }
+        }
+    }
+
+    private  List<LocalDateTime> getDays(LocalDateTime start, LocalDateTime end) {
+        List<LocalDateTime> result = new ArrayList();
+        while (start.isBefore(end)) {
+            result.add(start);
+            start=start.plusDays(1);
+        }
+        result.add(start);
+        return result;
+    }
+
 }
 

+ 52 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/SubProjectController.java

@@ -1,6 +1,8 @@
 package com.management.platform.controller;
 
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.entity.ProjectTimer;
 import com.management.platform.entity.SubProject;
@@ -17,6 +19,8 @@ import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * <p>
@@ -38,13 +42,48 @@ public class SubProjectController {
     @Resource
     private UserMapper userMapper;
 
+    /**
+     * 复制子项目到其他项目下
+     */
+    @RequestMapping("/copyToProjects")
+    public HttpRespMsg copyToProjects(Integer projectId, String targetProjectIds) {
+        HttpRespMsg msg = new HttpRespMsg();
+        List<SubProject> subProjects = subProjectService.list(new QueryWrapper<SubProject>().eq("project_id", projectId));
+        if (subProjects.size() == 0) {
+            msg.setError("该项目下没有子项目");
+        } else {
+            System.out.println(targetProjectIds);
+            JSONArray array = JSON.parseArray(targetProjectIds);
+            List<SubProject> batchList = new ArrayList<>();
+            for (int i =0; i < array.size(); i++) {
+                String targetProjectId = array.getString(i);
+                for (SubProject subProject : subProjects) {
+                    SubProject newSubProject = new SubProject();
+                    newSubProject.setCompanyId(subProject.getCompanyId());
+                    newSubProject.setProjectId(Integer.parseInt(targetProjectId));
+                    newSubProject.setName(subProject.getName());
+                    newSubProject.setStatus(subProject.getStatus());
+                    newSubProject.setCode(subProject.getCode());
+                    batchList.add(newSubProject);
+                }
+            }
+            subProjectService.saveBatch(batchList);
+        }
+
+        return msg;
+    }
+
     /**
      * 获取子项目列表
      */
     @RequestMapping("/list")
-    public HttpRespMsg list(Integer projectId) {
+    public HttpRespMsg list(Integer projectId,Boolean isReport) {
         HttpRespMsg msg = new HttpRespMsg();
-        msg.data = subProjectService.list(new QueryWrapper<SubProject>().eq("project_id", projectId));
+        QueryWrapper<SubProject> queryWrapper=new QueryWrapper<SubProject>().eq("project_id", projectId);
+        if(isReport!=null&&isReport){
+            queryWrapper.eq("status",1);
+        }
+        msg.data =subProjectService.list(queryWrapper);
         return msg;
     }
 
@@ -91,5 +130,16 @@ public class SubProjectController {
     public HttpRespMsg getTimeCost(String startDate, String endDate, Integer id) {
         return subProjectService.getTimeCost(startDate, endDate,id);
     }
+
+    @RequestMapping("/updateStatus")
+    public HttpRespMsg updateStatus(@RequestParam Integer id,Integer status){
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        SubProject byId = subProjectService.getById(id);
+        byId.setStatus(status);
+        if(!subProjectService.updateById(byId)){
+            httpRespMsg.setError("验证失败");
+        }
+        return httpRespMsg;
+    }
 }
 

+ 7 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/SubProject.java

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2022-03-16
+ * @since 2023-03-27
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -48,6 +48,12 @@ public class SubProject extends Model<SubProject> {
     @TableField("code")
     private String code;
 
+    /**
+     * 0-关闭 1-开启
+     */
+    @TableField("status")
+    private Integer status;
+
 
     @Override
     protected Serializable pkVal() {

+ 59 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java

@@ -378,6 +378,27 @@ public class DingDingServiceImpl implements DingDingService {
                 setting.setCompanyId(company.getId());
                 projectBasecostSettingMapper.insert(setting);
             }
+            //直接授权默认人员
+            User user = userMapper.selectList(new QueryWrapper<User>().eq("company_id", company.getId()).eq("is_active", 1)).get(0);
+            //todo: 生成初始测试项目及任务
+            Project project=new Project();
+            project.setCompanyId(company.getId());
+            project.setInchargerId(user.getId());
+            project.setInchargerName(user.getName());
+            project.setCreatorId(user.getId());
+            project.setCreatorName(user.getName());
+            project.setCreateDate(LocalDate.now());
+            project.setProjectCode("example");
+            project.setProjectName("示例项目");
+            //设置为公共项目,这样测试的用户都能填报
+            project.setIsPublic(1);
+            projectMapper.insert(project);
+            //todo: 生成初始项目相关日报审核人
+            ProjectAuditor projectAuditor=new ProjectAuditor();
+            projectAuditor.setAuditorId(user.getId());
+            projectAuditor.setAuditorName(user.getName());
+            projectAuditor.setProjectId(project.getId());
+            projectAuditorMapper.insert(projectAuditor);
             //todo: 生成项目报表服务默认条目
             Integer[] arrayInteger=new Integer[]{1,2,3,4,7};
             for (Integer integerItem : arrayInteger) {
@@ -386,9 +407,45 @@ public class DingDingServiceImpl implements DingDingService {
                 companyReport.setReportFormId(integerItem);
                 companyReportMapper.insert(companyReport);
             }
+            //todo: 生成初始项目相关示例任务分组/任务阶段以及示例任务
+            TaskGroup taskGroup = new TaskGroup();
+            taskGroup.setProjectId(project.getId())
+                    .setInchargerId(user.getId())
+                    .setName("项目阶段");
+            taskGroupMapper.insert(taskGroup);
+            Stages stage = new Stages();
+            stage.setGroupId(taskGroup.getId());
+            stage.setSequence(1);
+            stage.setProjectId(project.getId());
+            stage.setStagesName("工作开展");
+            stagesMapper.insert(stage);
+            Task task=new Task();
+            Integer oneDayHours = 8;
+            task.setCreateDate(LocalDate.now());
+            task.setProjectId(project.getId());
+            task.setCompanyId(company.getId());
+            task.setCreaterId(user.getId());
+            task.setCreaterName(user.getName());
+            task.setCreatorColor(user.getColor());
+            task.setExecutorId(user.getId());
+            task.setExecutorColor(user.getColor());
+            task.setExecutorName(user.getName());
+            task.setPlanHours(oneDayHours);
+            task.setStagesId(stage.getId());
+            task.setGroupId(taskGroup.getId());
+            task.setSeq(0);
+            task.setName("示例任务");
+            taskMapper.insert(task);
+            //任务执行人表也要插入,不然会导致编辑任务的时候执行人为空
+            TaskExecutor executor = new TaskExecutor();
+            executor.setTaskId(task.getId());
+            executor.setPlanHours(oneDayHours);
+            executor.setProjectId(project.getId());
+            executor.setExecutorId(user.getId());
+            executor.setExecutorColor(user.getColor());
+            executor.setExecutorName(user.getName());
+            taskExecutorMapper.insert(executor);
         }
-
-
         //钉钉企业外部关系表
         CompanyDingding dingding = new CompanyDingding();
         dingding.setCompanyId(company.getId());

+ 10 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -215,10 +215,10 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                     //只能看本人参与的项目
                     httpRespMsg.data = projectMapper.getOnlyJoinProjects(user.getId(), user.getCompanyId());
                 } else {
-                    //有权限的填报全部的进行中或者暂停的项目
+                    //有权限的填报全部的进行中项目
                     QueryWrapper<Project> queryWrapper = new QueryWrapper<>();
                     queryWrapper.eq("company_id", user.getCompanyId())
-                            .and(wrapper->wrapper.eq("status", 1).or().eq("status", 4))
+                            .eq("status", 1)
                             .orderByDesc("is_public").orderByAsc("id");
                     httpRespMsg.data = projectMapper.selectList(queryWrapper);
                 }
@@ -6763,12 +6763,17 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
             if(leaveSheets.size()>0){
                 for (LeaveSheet leaveSheet : leaveSheets) {
                     List<LocalDateTime> leaveDateList = getDays(leaveSheet.getStartDate().atTime(LocalTime.MIN), leaveSheet.getEndDate().atTime(LocalTime.MIN));
-                    if(leaveSheet.getTimeDays()<=1){
+                    /*if(leaveSheet.getTimeDays()<=1){
                         days-=1;
                     }else{
                         days-=leaveSheet.getTimeDays();
+                    }*/
+                    List<LocalDateTime> list = getDays(leaveSheet.getStartDate().atTime(LocalTime.now()), leaveSheet.getEndDate().atTime(LocalTime.now()));
+                    if(list.size()>1){
+                        days-=list.size();
+                    }else {
+                        days-=1;
                     }
-                    System.out.println(leaveDateList.size());
                     for (LocalDateTime localDateTime : leaveDateList) {
                         //todo: 请假区间当中有非工作日在之前被当作 不需填报 所以要加上 保证基数正确
                         if(!WorkDayCalculateUtils.isWorkDay(localDateTime.toLocalDate())){
@@ -6794,7 +6799,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 }
             }
             int num=0;
-            System.out.println(days);
+            System.out.println("总天数"+days);
             if(mapList!=null){
                 for (Map<String, Object> map : mapList) {
                     Map<String,Object> objectMap=new HashMap<>();

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

@@ -23,6 +23,7 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
 import org.apache.poi.ss.usermodel.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.client.RestTemplateBuilder;
 import org.springframework.http.*;
 import org.springframework.http.client.ClientHttpResponse;
 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
@@ -37,10 +38,14 @@ import org.springframework.web.multipart.MultipartFile;
 import sun.net.www.http.HttpClient;
 
 import javax.annotation.Resource;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
 import javax.servlet.http.HttpServletRequest;
 import java.io.*;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
 import java.sql.Timestamp;
 import java.text.DateFormat;
 import java.text.DecimalFormat;
@@ -200,6 +205,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     private CorpwxJobResultMapper corpwxJobResultMapper;
     @Autowired
     RestTemplate restTemplate;
+
     @Resource
     private ReportAuditLogService reportAuditLogService;
     @Resource
@@ -5441,7 +5447,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         param.put("data",array);
         System.out.println("推送过去的数据========"+array.toJSONString());
         HttpEntity<String> requestEntity = new HttpEntity<String>(param.toJSONString(),headers);
-        //过滤掉账号认证失败的时候抛出的401异常
         restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
             @Override
             public void handleError(ClientHttpResponse response) throws IOException {
@@ -5468,6 +5473,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
             }
         }
+
         return msg;
     }
 

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

@@ -1214,7 +1214,7 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                             JSONArray data = spTitle.getJSONArray("data");
                             for (int m = 0; m < data.size(); m++) {
                                 String leaveText = data.getJSONObject(m).getString("text");
-                                if (leaveText.startsWith("请假")) {
+                                if (leaveText.contains("假") || leaveText.contains("休")) {
                                     //获取对应位置的请假时间段
                                     String string = holiday.getJSONObject("sp_description").getJSONArray("data").getJSONObject(m).getString("text");
                                     String[] s = string.split(" |\\~");

+ 7 - 7
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java

@@ -403,7 +403,7 @@ public class TimingTask {
                     continue;
                 }
                 User user=optional.get();
-                System.out.println("泛微同步人员请假数据----"+user.getName());
+                /*System.out.println("泛微同步人员请假数据----"+user.getName());*/
                 if(compIds.contains(user.getCompanyId())){
                     LeaveSheet leaveSheet=new LeaveSheet();
                     leaveSheet.setCompanyId(user.getCompanyId());
@@ -447,9 +447,9 @@ public class TimingTask {
                         default:leaveType=8;
                     }
                     leaveSheet.setLeaveType(leaveType);
-                    leaveSheet.setProcinstId(String.valueOf(map.get("ID")));
+                    leaveSheet.setProcinstId(String.valueOf(map.get("id")));
                     leaveSheet.setGmtFinished(String.valueOf(map.get("gmtFinished")));
-                    Optional<LeaveSheet> first = oldLeaveSheetList.stream().filter(ol -> ol.getStartDate().isEqual(leaveSheet.getStartDate())&&ol.getEndDate().isEqual(leaveSheet.getEndDate())&& ol.getOwnerId().equals(leaveSheet.getOwnerId())&& ol.getProcinstId().equals(leaveSheet.getProcinstId())).findFirst();
+                    Optional<LeaveSheet> first = oldLeaveSheetList.stream().filter(ol -> ol.getStartDate().isEqual(leaveSheet.getStartDate())&&ol.getEndDate().isEqual(leaveSheet.getEndDate())&& ol.getOwnerId().equals(leaveSheet.getOwnerId())&&(ol.getProcinstId()!=null&& ol.getProcinstId().equals(leaveSheet.getProcinstId()))).findFirst();
                     if(first.isPresent()){
                         leaveSheet.setId(first.get().getId());
                     }
@@ -502,9 +502,9 @@ public class TimingTask {
                     businessTrip.setGoBack(goBack);
                     Double dayCount =Double.valueOf(String.valueOf(map.get("dayCount")));
                     businessTrip.setDayCount(dayCount.intValue());
-                    businessTrip.setProcinstId((String) map.get("ID"));
+                    businessTrip.setProcinstId((String) map.get("id"));
                     businessTrip.setGmtFinished((String)map.get("gmtFinished"));
-                    Optional<BusinessTrip> first = oldBusinessTripList.stream().filter(ol -> ol.getStartDate().isEqual(businessTrip.getStartDate())&&ol.getEndDate().isEqual(businessTrip.getEndDate())&& ol.getOwnerId().equals(businessTrip.getOwnerId())&& ol.getProcinstId().equals(businessTrip.getProcinstId())).findFirst();
+                    Optional<BusinessTrip> first = oldBusinessTripList.stream().filter(ol -> ol.getStartDate().isEqual(businessTrip.getStartDate())&&ol.getEndDate().isEqual(businessTrip.getEndDate())&& ol.getOwnerId().equals(businessTrip.getOwnerId())&&(ol.getProcinstId()!=null&& ol.getProcinstId().equals(businessTrip.getProcinstId()))).findFirst();
                     if(first.isPresent()){
                         businessTrip.setId(first.get().getId());
                     }
@@ -658,11 +658,11 @@ public class TimingTask {
         List<WxCorpInfo> wxCorpInfos = wxCorpInfoMapper.selectList(null);
         for (WxCorpInfo wxCorpInfo : wxCorpInfos) {
             LocalDate now = LocalDate.now();
-            List<Task> taskList = taskMapper.selectList(new QueryWrapper<Task>().eq("company_id", wxCorpInfo.getCompanyId()).and(wrapper -> wrapper.eq("end_date", now).or().eq("end_date", now.plusDays(1))));
+            List<Task> taskList = taskMapper.selectList(new QueryWrapper<Task>().eq("company_id", wxCorpInfo.getCompanyId()).eq("task_status",0).and(wrapper -> wrapper.eq("end_date", now).or().eq("end_date", now.plusDays(1))));
             List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", wxCorpInfo.getCompanyId()));
             List<Integer> taskIds = taskList.stream().map(Task::getId).distinct().collect(Collectors.toList());
             taskIds.add(-1);
-            List<TaskExecutor> taskExecutorList = taskExecutorMapper.selectList(new QueryWrapper<TaskExecutor>().in("task_id", taskIds).eq("task_status",0));
+            List<TaskExecutor> taskExecutorList = taskExecutorMapper.selectList(new QueryWrapper<TaskExecutor>().in("task_id", taskIds));
             for (Task task : taskList) {
                 List<String> list = taskExecutorList.stream().filter(tl -> tl.getTaskId().equals(task.getId())).map(TaskExecutor::getExecutorId).distinct().collect(Collectors.toList());
                 String corpUserid = userList.stream().filter(ul -> list.contains(ul.getId())).map(User::getCorpwxUserid).distinct().collect(Collectors.joining(","));

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

@@ -139,7 +139,7 @@
     <select id="getOnlyJoinProjects" resultType="java.util.Map">
         SELECT id, project_code AS projectCode, project_name AS projectName, status, task_gp_incharge as taskGpIncharge, project_desc as projectDesc
         FROM project
-        WHERE (status = 1 or status = 4) and (id IN (
+        WHERE status = 1 and (id IN (
             SELECT project_id
             FROM participation
             WHERE user_id = #{userId}
@@ -1582,7 +1582,7 @@
 
     <select id="selectNearProject" resultType="com.management.platform.entity.Project">
         select * from project
-        where id in (
+        where status = 1 and id in (
             SELECT project_id
             from (
                 SELECT project_id

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

@@ -460,7 +460,7 @@
 
     <!--根据日期获取报告上传人-->
     <select id="getReportNameByDate" resultType="java.util.Map">
-        SELECT DISTINCT b.id, b.name, a.state, a.department_audit_state as departmentAuditState, IFNULL(sum(a.working_time),0) as workingTime,a.evaluate as evaluate
+        SELECT DISTINCT b.id, b.name, IF (MAX(state) = 1, MIN(state), MAX(state)) AS state, a.department_audit_state as departmentAuditState, IFNULL(sum(a.working_time),0) as workingTime,a.evaluate as evaluate
         FROM report AS a
         JOIN user AS b ON a.creator_id=b.id
         left join project on project.id = a.project_id

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

@@ -9,11 +9,12 @@
         <result column="project_id" property="projectId" />
         <result column="company_id" property="companyId" />
         <result column="code" property="code" />
+        <result column="status" property="status" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, name, project_id, company_id, code
+        id, name, project_id, company_id, code, status
     </sql>
     <!--获取某个项目内子项目的工时成本统计-->
     <select id="getTimeCost" resultType="java.util.Map">

+ 2 - 0
fhKeeper/formulahousekeeper/octopus/src/views/Home.vue

@@ -15,6 +15,8 @@
                 </div>
             </el-col>
             <el-col :span="10" class="userinfo">
+                <!-- <el-button type="text" style="margin-right:30px; color:#fff" @click="downLoadLog" >下载工时管家日志</el-button> -->
+                <a style="margin-right:30px; text-decoration:none; color:#fff" href="/api/common/downLoadLog">下载工时管家日志</a>
                 <el-dropdown trigger="hover" style="margin-right:30px;">
                     <span class="el-dropdown-link userinfo-inner">
                         <i class="el-icon-user" style="font-size:18px" ></i>

+ 2 - 0
fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue

@@ -34,6 +34,8 @@
             
             <el-table-column prop="expirationDate" label="有效期"  min-width="160" align="center">
             </el-table-column>
+            <el-table-column prop="remainingValidDays" label="剩余有效天数"  min-width="160" align="center">
+            </el-table-column>
             <el-table-column prop="wxCorpid" label="企业微信"  min-width="160" align="center">
             </el-table-column>
             <el-table-column prop="dingdingCorpid" label="钉钉"  min-width="160" align="center">

+ 45 - 4
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CommonUploadController.java

@@ -1,17 +1,39 @@
 package com.management.platform.controller;
 
+import com.management.platform.entity.Company;
 import com.management.platform.util.HttpRespMsg;
+import fr.opensagres.poi.xwpf.converter.pdf.PdfConverter;
+import fr.opensagres.poi.xwpf.converter.pdf.PdfOptions;
+import fr.opensagres.xdocreport.document.IXDocReport;
+import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
+import fr.opensagres.xdocreport.template.IContext;
+import fr.opensagres.xdocreport.template.TemplateEngineKind;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.util.FileCopyUtils;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.math.BigDecimal;
+import java.net.URLEncoder;
+import java.nio.channels.FileChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 import java.util.UUID;
 
 @RestController
@@ -21,6 +43,8 @@ public class CommonUploadController {
     Logger logger = LogManager.getLogger(org.apache.logging.log4j.LogManager.ROOT_LOGGER_NAME);
     @Value(value = "${upload.path}")
     private String path;
+    @Value(value = "${logDownLoad.path}")
+    private String logDownLoadPath;
 
     @RequestMapping(value="uploadFile")
     public HttpRespMsg uploadFile(MultipartFile multipartFile) {
@@ -57,4 +81,21 @@ public class CommonUploadController {
 
         return msg;
     }
+    @RequestMapping("/downLoadLog")
+    public ResponseEntity<byte[]> downLoadLog() throws IOException {
+        // 🧐🧐🧐读取本地的文件
+        // 获取File对象
+        File readFile=new File(logDownLoadPath);
+        // 🐳🐳🐳设置响应头,把文件名称放入响应头中,确保文件可下载
+        HttpHeaders headers = new HttpHeaders();
+        headers.set("Content-Disposition", "attachment;filename=" + URLEncoder.encode(readFile.getName(), "UTF-8"));
+        // 🐳🐳🐳设置内容类型为「application/octet-stream」二进制流
+        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
+
+        Path path = Paths.get(readFile.toURI());
+        // 获取File对象的字节码文件
+        byte[] bytes = Files.readAllBytes(path);
+        //return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
+         return ResponseEntity.ok().headers(headers).body(bytes);
+    }
 }

+ 3 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CompanyController.java

@@ -98,6 +98,9 @@ public class CompanyController {
                     re.setCanDelete(1);
                 }
             }
+            LocalDateTime expirationDate = re.getExpirationDate();
+            Duration duration=Duration.between(now,expirationDate);
+            re.setRemainingValidDays(duration.toDays()>=0?duration.toDays():0);
         });
         List<Integer> collect = records.stream().map(Company::getId).collect(Collectors.toList());
         if (collect.size() > 0) {

+ 3 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/entity/Company.java

@@ -153,6 +153,9 @@ public class Company extends Model<Company> {
     @TableField(exist = false)
     private Integer canDelete;
 
+    @TableField(exist = false)
+    private long remainingValidDays;
+
     @Override
     protected Serializable pkVal() {
         return this.id;

+ 3 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/resources/application.yml

@@ -82,6 +82,9 @@ mybatis:
 upload:
   path: C:/upload/
 
+logDownLoad:
+  path: C:/upload/wt_print.log
+
 ##actuator健康检查配置
 management:
   security:

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue

@@ -124,7 +124,7 @@
                 this.isCorpWX = true;
             } 
             let href = window.location.href;
-            this.wxHrefFlg = href.indexOf('worktime.ttkuaiban.com') != '-1'
+            this.wxHrefFlg = href.indexOf('//worktime.ttkuaiban.com') != '-1'
             if (this.isCorpWX) {
                 //企业微信环境下,尝试自动登录
                 //判断企业微信,是否存在授权

+ 4 - 4
fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue

@@ -1057,10 +1057,10 @@ export default {
                   strObj.project = '所属项目'
                   strObj.flg = true
                 }
-                if(!this.invoiceList[i].invoiceType || this.invoiceList[i].invoiceType == '') {
-                  strObj.fptype = '发票种类'
-                  strObj.flg = true
-                }
+                // if(!this.invoiceList[i].invoiceType || this.invoiceList[i].invoiceType == '') {
+                //   strObj.fptype = '发票种类'
+                //   strObj.flg = true
+                // }
                 if(!this.invoiceList[i].expenseType || this.invoiceList[i].expenseType == '') {
                   strObj.fyType = '费用类型'
                   strObj.flg = true

+ 100 - 4
fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue

@@ -827,8 +827,15 @@
             </el-table-column> -->
             <el-table-column width="120" :label="$t('subprojectno')" prop="code"></el-table-column>
             <el-table-column prop="name" :label="$t('names')" ></el-table-column>
-            <el-table-column :label="$t('operation')" width="150">
-                <template slot-scope="scope" >
+            <el-table-column :label="$t('operation')" width="220">
+                <template  slot-scope="scope" >
+                    <el-switch
+                    @change="changSubStatus(scope.row)"
+                    v-model="scope.row.status"
+                    active-color="#13ce66"
+                    inactive-color="#ff4949"
+                    style="margin-right: 20px;">
+                    </el-switch>
                     <el-button size="small" type="primary" @click="addNewSubProject(scope.row)">{{ $t('bian-ji') }}</el-button>
                     <el-button size="small" type="danger" @click="deleteSubPro(scope.row)">{{ $t('btn.delete') }}</el-button>
                 </template>
@@ -836,10 +843,25 @@
 
             </el-table>
             <div slot="footer" class="dialog-footer">
+                <el-button type="default" :disabled="subProjectList.length == 0" @click="forSubProjectlist = list.filter(a=>a.id != currentProject.id);copySubPListVisible = true;" style="align:left;">复制到其他项目</el-button>
                 <el-button type="primary" @click="subProjectVisible = false" >{{ $t('Shutdown') }}</el-button>
                 <el-button type="primary" @click="addNewSubProject()" >{{ $t('addsubitems') }}</el-button>
             </div>
         </el-dialog>
+        <!--选择项目弹出框-->
+        <el-dialog title="选择要复制到的目标项目" v-if="copySubPListVisible" :visible.sync="copySubPListVisible" :close-on-click-modal="false" customClass="customWidth" width="700px">
+            <el-table ref="projectForSubCopy" border @cell-mouse-enter="hoverCall" @cell-mouse-leave="handCall" :cell-class-name="tableCellClassName" :data="forSubProjectlist" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;" @selection-change="checkedProlistForSub" @sort-change="tableSort">
+                <el-table-column type="selection" width="60" >
+                </el-table-column>
+                <el-table-column prop="projectCode" :label="$t('Itemno')"  width="150"></el-table-column>
+                <el-table-column prop="projectName" :label="$t('headerTop.projectName')" min-width="250">
+                </el-table-column>
+            </el-table>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click.native="copySubPListVisible = false">{{ $t('btn.cancel') }}</el-button>
+                <el-button type="primary" @click="submitCopySubProject()" >{{ $t('btn.submit') }}</el-button>
+            </div>
+        </el-dialog>
 
 
         <!-- 新增子项目弹出框 -->
@@ -1371,6 +1393,8 @@ a {
         },
         data() {
             return {
+                forSubProjectlist:[],
+                copySubPListVisible: false,
                 projectManagerEdit: false,
                 canOnlyModParticipator: false,
                 curChangeProject:null,
@@ -1492,7 +1516,7 @@ a {
                 },
                 statusClf:null,
                 permissionsObj: {},
-
+                checkedPForSubArr:[],
                 checkedProjectArr: [],
                 checkedWuduDialog: false,
                 checkedWuduArr: [],
@@ -1602,6 +1626,41 @@ a {
             })
         },
         methods: {
+            submitCopySubProject() {
+                //获取选中的项目
+                if (this.checkedPForSubArr.length == 0) {
+                    this.$message(this.$t('defaultText.pleaseSelectSnItem'))
+                    return
+                }
+                
+                var targetPIds = JSON.stringify(this.checkedPForSubArr.map((a)=>{return a.id}));
+                //http post请求提交
+                this.http.post('/sub-project/copyToProjects', {
+                    projectId: this.currentProject.id,
+                    targetProjectIds: targetPIds,
+                },
+                res => {
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: '操作成功',
+                            type: "success"
+                        });
+                        this.copySubPListVisible = false;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+
+            },
             confirmChangeStage() {
                 let currentStageName = ''
                 for(var i in this.phaseList) {
@@ -2032,6 +2091,9 @@ a {
                     })
                 })
             },
+            checkedProlistForSub(e) {
+                this.checkedPForSubArr = e;
+            },
             // 批量修改自定义维度
             checkedWudulist(e){
                 // console.log(e);
@@ -3599,6 +3661,9 @@ a {
                 res => {
                     if (res.code == "ok") {
                         this.subProjectList = res.data;
+                        for(let i = 0; i < this.subProjectList.length; i++){
+                            this.subProjectList[i].status=this.subProjectList[i].status==1?true:false
+                        }
                     } else {
                         this.$message({
                         message: res.msg,
@@ -4592,7 +4657,35 @@ a {
                     arr.push(obj.id)
                     this.addForm.bu = arr.join(",")
                 }
-            }
+            },
+            changSubStatus(subProject) {
+                this.http.post('/sub-project/updateStatus',{ 
+                    id: subProject.id,
+                    status: subProject.status?1:0
+                },
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: subProject.status?"开启成功":"关闭成功",
+                            type: "success"
+                        });
+                        this.subProject(this.currentProject);
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                    }
+                );
+            },
         },
         created() {
             let height = window.innerHeight;
@@ -4687,6 +4780,9 @@ a {
     overflow: hidden;
     text-overflow: ellipsis;
 }
+.dialogSwitch {
+    
+}
 
 // 111
 

+ 14 - 11
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -487,18 +487,20 @@
                                 <el-option v-for="item in domain.stages" :key="item.id" :label="item.stagesName" :value="item.stagesName"></el-option>
                             </el-select>
                         </el-form-item>
-                        <el-form-item :label="user.companyId==781? $t('other.reviewer') : $t('other.projectAuditor')" v-if="user.timeType.reportAuditType != 3 && user.timeType.reportAuditType != 5">
-                            
-                            <el-select v-model="domain.projectAuditorId" :disabled="!domain.canEdit" @change="$forceUpdate()" v-if="user.userNameNeedTranslate != '1'" style="width:200px;">
-                                <el-option v-for="item in domain.auditUserList" :label="item.auditorName" :value="item.auditorId" :key="item.id">
-                                    <span style="float: left" v-if="user.userNameNeedTranslate == '1'"><ww-open-data type='userName' :openid='item.auditorName'></ww-open-data></span>
-                                    <span style="float: left" v-if="user.userNameNeedTranslate != '1'">{{item.auditorName}}</span>
-                                </el-option>
-                            </el-select>
+                        <div v-if="user.timeType.reportAuditType != 3 && user.timeType.reportAuditType != 5 && domain.auditUserList">
+                            <el-form-item :label="user.companyId==781? $t('other.reviewer') : $t('other.projectAuditor')" v-if="user.timeType.reportAuditType != 3 && user.timeType.reportAuditType != 5 && domain.auditUserList.length > 0">
+                                
+                                <el-select v-model="domain.projectAuditorId" :disabled="!domain.canEdit" @change="$forceUpdate()" v-if="user.userNameNeedTranslate != '1'" style="width:200px;">
+                                    <el-option v-for="item in domain.auditUserList" :label="item.auditorName" :value="item.auditorId" :key="item.id">
+                                        <span style="float: left" v-if="user.userNameNeedTranslate == '1'"><ww-open-data type='userName' :openid='item.auditorName'></ww-open-data></span>
+                                        <span style="float: left" v-if="user.userNameNeedTranslate != '1'">{{item.auditorName}}</span>
+                                    </el-option>
+                                </el-select>
 
-                            <selectCat v-if="user.userNameNeedTranslate == '1'" :size="'medium'" :subject="domain.auditUserList" :idx="index" :subjectId="domain.projectAuditorId" ref="selectCat" :flg="domain.projectAuditorId ? true : false" :flgs="true" @selectCatCli="selectCatCli" :disabled="!domain.canEdit"></selectCat>
+                                <selectCat v-if="user.userNameNeedTranslate == '1'" :size="'medium'" :subject="domain.auditUserList" :idx="index" :subjectId="domain.projectAuditorId" ref="selectCat" :flg="domain.projectAuditorId ? true : false" :flgs="true" @selectCatCli="selectCatCli" :disabled="!domain.canEdit"></selectCat>
 
-                        </el-form-item>
+                            </el-form-item>
+                        </div>
                         <!-- 111111 -->
                         <el-form-item :label="$t('other.reviewer')" v-if="user.timeType.reportAuditType == 3">
                             <el-select v-model="domain.auditorFirst" :placeholder="$t('di-yi-shen-he-ren')" :disabled="!domain.canEdit" style="width:200px;">
@@ -3432,7 +3434,8 @@
                 // console.log('projectId===',domain.projectId);
                 domain.projectName = this.fillProjectList.filter(p=>p.id == domain.projectId)[0].projectName;
                 this.http.post('/sub-project/list',{
-                    projectId: domain.projectId
+                    projectId: domain.projectId,
+                    isReport: true
                 },
                 res => {
                     //清空之前选中的子项目

BIN
fhKeeper/formulahousekeeper/timesheet_h5/src/assets/img/chong.png


+ 7 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/router/index.js

@@ -210,6 +210,13 @@ const router = new Router({
         },
         component: () => import("@/views/exaLeave/awayOffice")
     },
+    {
+        path: "/clearStorage",
+        meta: {
+            title: "清空存储"
+        },
+        component: () => import("@/views/test/clearStorage")
+    },
     {
         path: "/my",
         component: () => import("@/views/my/index"),

+ 3 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue

@@ -90,6 +90,9 @@
                 }
             }
             let userss = JSON.parse(localStorage.userInfo)
+            if(userss.companyId == '3454') {
+                alert(`isMobFirstLogin = ${userss.isMobFirstLogin} $ userss.createTime = ${userss.createTime[0]} $ userss.roleName = ${userss.roleName}`)
+            }
             if(userss.isMobFirstLogin == 1 && userss.createTime[0] > '2022' && userss.roleName == '超级管理员') {
                 // 第一次登陆显示
                 this.previewPicture()

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/login/index.vue

@@ -216,6 +216,7 @@
             if (localStorage.userInfo != null) {
                 this.$router.push("/index").catch(err => { console.log(err, '错误4')});
             }
+            // localStorage.clear()
 
             // 米莱的用工号登录
             let windowHerf = window.location.href

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

@@ -123,7 +123,7 @@
                         <div v-if="item1.multiWorktime == 1">
                             <div>
                                 项目时长:<span style="margin-right:10px;">{{item1.time.toFixed(1)}}h</span>
-                            <div class="button" v-if="item1.isOvertime == 1">加班<span v-if="item1.overtimeHours">{{item1.overtimeHours.toFixed(1)}}h</span></div>
+                            <div class="button" v-if="item1.isOvertime == 1" style="width: 2.3rem">加班<span v-if="item1.overtimeHours">{{item1.overtimeHours.toFixed(1)}}h</span></div>
                             </div>
                             <div style="position:relative;border:#ccc 0.5px solid;padding:3px;margin:5px 0px;" v-for="(timeItem, index) in item1.worktimeList" :key="index" >
                                 <div class="project_time">时长:

+ 78 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/test/clearStorage.vue

@@ -0,0 +1,78 @@
+<template>
+  <div>
+    <div class="clear">
+      <div class="clear_img">
+        <img src="../../assets/img/chong.png" alt="">
+      </div>
+      <div style="width: 100%;text-align: center;">
+        清除成功
+      </div> 
+      <div class="clear_text">
+        页面将在{{ nowTime }}秒后返回登录页
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {},
+  components: {},
+  data() {
+    return {
+      timer: null,
+      nowTime: 3
+    };
+  },
+  computed: {},
+  watch: {},
+  created() {},
+  mounted() {
+    clearInterval(this.timer);
+    this.countdown()
+  },
+  methods: {
+    countdown() {
+      let that = this;
+      this.timer = setInterval(() => {
+        if(that.nowTime != 0) {
+          that.nowTime = +that.nowTime - 1
+        } else {
+          localStorage.clear()
+          this.$router.push("/login");
+        }
+      }, 1000);
+    }
+  },
+	beforeDestroy() {
+	  if (this.timer) {
+	    clearInterval(this.timer);
+	  }
+  },
+};
+</script>
+
+<style scoped>
+  .clear {
+    width: 100%;
+    display: flex;
+    justify-content: center;
+    font-size: 25px;
+    margin-top: 100px;
+    flex-wrap: wrap;
+    color: #a0a0a0;
+  }
+  .clear_img {
+    width: 100px;
+    height: 100px;
+    margin-bottom: 20px;
+  }
+  .clear_img img {
+    width: 100%;
+  }
+  .clear_text {
+    margin-top: 20px;
+    font-size: 18px;
+    color: #a0a0a0;
+  }
+</style>