Bläddra i källkod

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

Lijy 2 år sedan
förälder
incheckning
e522bde77d
70 ändrade filer med 2364 tillägg och 1171 borttagningar
  1. 9 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/CompanyController.java
  2. 9 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/CompanyDingdingController.java
  3. 181 109
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/DingDingController.java
  4. 5 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ExpenseSheetController.java
  5. 8 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  6. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportExtraDegreeController.java
  7. 51 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  8. 5 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Company.java
  9. 16 9
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/CompanyDingding.java
  10. 10 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  11. 14 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/TimelinessRateVO.java
  12. 3 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  13. 6 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/CompanyDingdingService.java
  14. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/CompanyService.java
  15. 13 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/DingDingService.java
  16. 6 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExpenseSheetService.java
  17. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java
  18. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportExtraDegreeService.java
  19. 298 155
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/CompanyDingdingServiceImpl.java
  20. 173 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/CompanyServiceImpl.java
  21. 209 178
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java
  22. 260 15
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ExpenseSheetServiceImpl.java
  23. 114 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  24. 30 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportExtraDegreeServiceImpl.java
  25. 5 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  26. 4 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/SysRoleServiceImpl.java
  27. 7 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  28. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  29. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java
  30. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application-dev.yml
  31. 6 8
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application-mld.yml
  32. 4 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml
  33. 4 3
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/CompanyDingdingMapper.xml
  34. 14 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  35. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml
  36. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/人员导入模板.xlsx
  37. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/员工工时导入模板.xlsx
  38. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/员工工时统计模板.xlsx
  39. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/员工项目分摊比例导入模板.xlsx
  40. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/工时管家使用说明_基础版.docx
  41. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/工时管家使用说明_建筑工程专业版.docx
  42. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/工时管家使用说明_项目管理专业版.docx
  43. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/新增客户导入模板.xlsx
  44. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/研究中心导入模板.xlsx
  45. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/资源需求导入模板.xlsx
  46. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/项目任务导入模板.xlsx
  47. BIN
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/项目导入模板.xlsx
  48. BIN
      fhKeeper/formulahousekeeper/management-platform/人员导入模板.xlsx
  49. 11 1
      fhKeeper/formulahousekeeper/octopus/src/routes.js
  50. 158 0
      fhKeeper/formulahousekeeper/octopus/src/views/migrateData/migrateData.vue
  51. 10 5
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CompanyController.java
  52. 21 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/ReportController.java
  53. 40 49
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/entity/Report.java
  54. 19 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/mapper/CustomerInfoMapper.java
  55. 16 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/mapper/ProjectAuditorMapper.java
  56. 4 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/CompanyService.java
  57. 16 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/ReportService.java
  58. 185 7
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/impl/CompanyServiceImpl.java
  59. 20 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  60. 7 477
      fhKeeper/formulahousekeeper/ops-platform/src/main/resources/mapper/ReportMapper.xml
  61. 2 0
      fhKeeper/formulahousekeeper/timesheet/src/permissions.js
  62. 25 0
      fhKeeper/formulahousekeeper/timesheet/src/views/centerManage/centerManage.vue
  63. 146 24
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue
  64. 92 8
      fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue
  65. 35 33
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  66. 42 11
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue
  67. 1 1
      fhKeeper/formulahousekeeper/timesheet_h5/public/index.html
  68. 8 8
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  69. 14 16
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/project/index.vue
  70. 10 10
      fhKeeper/formulahousekeeper/timesheet_h5/vue.config.js

+ 9 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/CompanyController.java

@@ -1,8 +1,10 @@
 package com.management.platform.controller;
 
 
+import com.management.platform.service.CompanyService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
-
 import org.springframework.web.bind.annotation.RestController;
 
 /**
@@ -16,6 +18,11 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RequestMapping("/company")
 public class CompanyController {
-
+    @Autowired
+    private CompanyService companyService;
+    @RequestMapping("/dataMigration")
+    public HttpRespMsg dataMigration(Integer oldCompanyId,Integer targetCompanyId){
+       return companyService.dataMigration(oldCompanyId,targetCompanyId);
+    }
 }
 

+ 9 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/CompanyDingdingController.java

@@ -1,17 +1,19 @@
 package com.management.platform.controller;
 
 
+import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
 import com.management.platform.entity.CompanyDingding;
 import com.management.platform.mapper.CompanyDingdingMapper;
 import com.management.platform.service.CompanyDingdingService;
 import com.management.platform.util.HttpRespMsg;
 import org.springframework.web.bind.annotation.RequestMapping;
-
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * <p>
@@ -49,7 +51,12 @@ public class CompanyDingdingController {
     @RequestMapping("/sendOAMsg")
     public HttpRespMsg sendOAMsg(String corpid, String userDingId) {
         CompanyDingding companyDingding = companyDingdingMapper.selectById(corpid);
-        companyDingdingService.sendOAMsg(companyDingding, "040534176023851922", null);
+        List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+        OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+        item.setKey("测试");
+        item.setValue("试试成功没");
+        form.add(item);
+        companyDingdingService.sendOAMsg(companyDingding, "040534176023851922", "OA提醒测试","这是一次测试",form,null);
         return new HttpRespMsg();
     }
 }

+ 181 - 109
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/DingDingController.java

@@ -16,11 +16,14 @@ import com.management.platform.util.*;
 import com.taobao.api.ApiException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 import java.util.List;
 import java.util.Map;
@@ -33,11 +36,16 @@ import java.util.stream.Collectors;
 public class DingDingController {
 
     private final Logger bizLogger = LoggerFactory.getLogger(getClass());
-    String token = "cf776d62c5fb3508b5d8c2cbb9f3df0c";
+
     String aesKey = "ktmbamhymjsf60ndwp6n81mnu92847oynsgj9e0zr9v";
+    String token = "cf776d62c5fb3508b5d8c2cbb9f3df0c";
+
     String suiteKey = "suitejwoq9dw4bxv4stdb";
     String suiteSecret = "o-TWFLkFe8YbJsa_025JOj_JEWydP5GR3eigt1Yn1rtx5dTcryTJiSA6KEih3Bi4";
 
+    @Value("${configEnv.isPrivateDeploy}")
+    private boolean isPrivateDeploy;
+
     @Resource
     private DingDingService dingDingService;
     static ExecutorService executorService = Executors.newCachedThreadPool();
@@ -56,14 +64,15 @@ public class DingDingController {
             @RequestParam(value = "signature") String signature,
             @RequestParam(value = "timestamp") String timestamp,
             @RequestParam(value = "nonce") String nonce,
-            @RequestBody(required = false) JSONObject body
+            @RequestBody(required = false) JSONObject body,
+            @RequestParam(value = "innerAppKey", required = false) String innerAppKey
     ) {
-        System.out.println("接收到ding call back");
+        System.out.println("接收到ding call back"+(isPrivateDeploy?", innerAppKey="+innerAppKey:""));
         if (body == null) {
             System.out.println("Body为NULL !!!!!,直接返回success");
             DingCallbackCrypto callbackCrypto = null;
             try {
-                callbackCrypto = new DingCallbackCrypto(token, aesKey, suiteKey);
+                callbackCrypto = new DingCallbackCrypto(token, aesKey, isPrivateDeploy?innerAppKey:suiteKey);
                 Map<String, String> successMap = callbackCrypto.getEncryptedMap("success");
                 LocalDateTime now = LocalDateTime.now();
                 DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
@@ -83,7 +92,7 @@ public class DingDingController {
             //      此时OWNER_KEY为应用的APP_KEY(企业内部应用)或SUITE_KEY(三方应用)。
             // 2、调用订阅事件接口订阅的事件为企业级事件推送,
             //      此时OWNER_KEY为:企业的CORP_ID(企业内部应用)或SUITE_KEY(三方应用)
-            DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(token, aesKey, suiteKey);
+            DingCallbackCrypto callbackCrypto = new DingCallbackCrypto(token, aesKey, isPrivateDeploy?innerAppKey:suiteKey);
             String encryptMsg = body.getString("encrypt");
             String decryptMsg = callbackCrypto.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
 
@@ -99,126 +108,162 @@ public class DingDingController {
             } else if ("user_add_org".equals(eventType)) {
                 // 处理通讯录用户增加事件
                 bizLogger.info("发生了:" + eventType + "事件");
+                String corpId = eventJson.getString("CorpId");
+                JSONArray userIdArray = eventJson.getJSONArray("UserId");
+                dingDingService.userAddOrg(corpId, userIdArray);
             } else {
-                // 添加其他已注册的
                 bizLogger.info("发生了:" + eventType + "事件");
-                //推送suit ticket事件
-                /**
-                 * {
-                 *   "SuiteKey": "xxxxxx",
-                 *   "EventType": "suite_ticket ",
-                 *   "TimeStamp": 123456,
-                 *   "SuiteTicket": "xxxxxx"
-                 * }
-                 */
-                if ("suite_ticket".equals(eventType)) {
-                    System.out.println("==推送SuiteTicket===");
-                    dingDingService.updateSuiteTicket(eventJson.getString("SuiteTicket"));
-                } else if ("tmp_auth_code".equals(eventType)) {
-                    //企业授权开通应用
+                if ("user_leave_org".equals(eventType)) {
+                    String corpId = eventJson.getString("CorpId");
+                    JSONArray userIdArray = eventJson.getJSONArray("UserId");
+                    Long timeStamp = eventJson.getLong("TimeStamp");
+                    LocalDateTime time = LocalDateTime.ofEpochSecond(timeStamp/1000,0, ZoneOffset.ofHours(8));
+                    dingDingService.userLeaveOrg(corpId, userIdArray, time.toLocalDate());
+                } else {
+                    // 添加其他已注册的
+                    //推送suit ticket事件
                     /**
                      * {
-                     *    "TimeStamp":1553709079062,
-                     *    "AuthCode": "xxxxxx",
-                     *    "AuthCorpId":"xxxxxx",
-                     *    "EventType":"tmp_auth_code",
-                     *    "SuiteKey":"xxxxxx"
-                     *  }
+                     *   "SuiteKey": "xxxxxx",
+                     *   "EventType": "suite_ticket ",
+                     *   "TimeStamp": 123456,
+                     *   "SuiteTicket": "xxxxxx"
+                     * }
                      */
-                    System.out.println("==企业授权开通应用===");
+                    if ("suite_ticket".equals(eventType)) {
+                        System.out.println("==推送SuiteTicket===");
+                        dingDingService.updateSuiteTicket(eventJson.getString("SuiteTicket"));
+                    } else if ("tmp_auth_code".equals(eventType)) {
+                        //企业授权开通应用
+                        /**
+                         * {
+                         *    "TimeStamp":1553709079062,
+                         *    "AuthCode": "xxxxxx",
+                         *    "AuthCorpId":"xxxxxx",
+                         *    "EventType":"tmp_auth_code",
+                         *    "SuiteKey":"xxxxxx"
+                         *  }
+                         */
+                        System.out.println("==企业授权开通应用===");
 
-                } else if ("SYNC_HTTP_PUSH_HIGH".equals(eventType)) {
-                    /**
-                     * {"EventType":"SYNC_HTTP_PUSH_HIGH",
-                     * "bizData":[{"gmt_create":1624757980000,"biz_type":2,"open_cursor":0,
-                     * "subscribe_id":"16822005_0","id":15275,"gmt_modified":1624757980000,
-                     * "biz_id":"16822005",
-                     * "biz_data":"{\"syncAction\":\"suite_ticket\",
-                     * \"suiteTicket\":\"IaIbq7a9S36R2uAbUh02VNlWoMB2LkorhyFCFhdpSWxJPZhIsIEhxIoIS7HQRn0zxMGh57gGd56RRh2Ntmcuzq\",
-                     * \"syncSeq\":\"AE815E12E6908BDEE8CA8D33A9\"}","corp_id":"ding169917db7f1ee5f435c2f4657eb6378f","status":0}]}
-                     */
-                    JSONArray bizData = eventJson.getJSONArray("bizData");
-                    if (bizData.size() > 0) {
-                        //优先处理suite_ticket推送
-                        for (int i=0;i<bizData.size(); i++) {
-                            JSONObject bizItem = bizData.getJSONObject(i);
-                            int bizType = bizItem.getInteger("biz_type");
-                            if (bizType == 2) {
-                                //SuteTicket推送事件
-                                JSONObject biz_data = JSONObject.parseObject(bizItem.getString("biz_data"));
-                                String syncAction = biz_data.getString("syncAction");
-                                if ("suite_ticket".equals(syncAction)) {
-                                    System.out.println("==Push 推送事件 suite_ticket===");
-                                    dingDingService.updateSuiteTicket(biz_data.getString("suiteTicket"));
+                    } else if ("SYNC_HTTP_PUSH_HIGH".equals(eventType)) {
+                        /**
+                         * {"EventType":"SYNC_HTTP_PUSH_HIGH",
+                         * "bizData":[{"gmt_create":1624757980000,"biz_type":2,"open_cursor":0,
+                         * "subscribe_id":"16822005_0","id":15275,"gmt_modified":1624757980000,
+                         * "biz_id":"16822005",
+                         * "biz_data":"{\"syncAction\":\"suite_ticket\",
+                         * \"suiteTicket\":\"IaIbq7a9S36R2uAbUh02VNlWoMB2LkorhyFCFhdpSWxJPZhIsIEhxIoIS7HQRn0zxMGh57gGd56RRh2Ntmcuzq\",
+                         * \"syncSeq\":\"AE815E12E6908BDEE8CA8D33A9\"}","corp_id":"ding169917db7f1ee5f435c2f4657eb6378f","status":0}]}
+                         */
+                        JSONArray bizData = eventJson.getJSONArray("bizData");
+                        if (bizData.size() > 0) {
+                            //优先处理suite_ticket推送
+                            for (int i=0;i<bizData.size(); i++) {
+                                JSONObject bizItem = bizData.getJSONObject(i);
+                                int bizType = bizItem.getInteger("biz_type");
+                                if (bizType == 2) {
+                                    //SuteTicket推送事件
+                                    JSONObject biz_data = JSONObject.parseObject(bizItem.getString("biz_data"));
+                                    String syncAction = biz_data.getString("syncAction");
+                                    if ("suite_ticket".equals(syncAction)) {
+                                        System.out.println("==Push 推送事件 suite_ticket===");
+                                        dingDingService.updateSuiteTicket(biz_data.getString("suiteTicket"));
+                                    }
                                 }
                             }
-                        }
-                        //再处理其他的情况
-                        for (int i=0;i<bizData.size(); i++) {
-                            JSONObject bizItem = bizData.getJSONObject(i);
-                            int bizType = bizItem.getInteger("biz_type");
-                            if (bizType == 4) {
-                                //企业授权变更,包含授权、解除授权、授权变更。
-                                JSONObject biz_data = JSONObject.parseObject(bizItem.getString("biz_data"));
-                                System.out.println("授权变更数据===@@");
-                                System.out.println(biz_data);
-                                String syncAction = biz_data.getString("syncAction");
-                                if ("org_suite_auth".equals(syncAction) || "org_suite_change".equals(syncAction)) {
-                                    //开通授权应用
-                                    JSONObject authCorpInfo = biz_data.getJSONObject("auth_corp_info");
-                                    String corp_name = authCorpInfo.getString("corp_name");
-                                    boolean isAuthenticated = authCorpInfo.getBooleanValue("is_authenticated");
-                                    Long agentId = null;
-                                    if ("org_suite_auth".equals(syncAction)) {
-                                        System.out.println("==Push 推送事件 开通授权应用===" + corp_name);
-                                        //此处在授权开通时强制赋值
-                                        isAuthenticated = true;
-                                        JSONObject authInfo = biz_data.getJSONObject("auth_info");
-
-                                        if (authInfo != null) {
-                                            JSONArray agents = authInfo.getJSONArray("agent");
-                                            for (int j=0;j<agents.size(); j++) {
-                                                JSONObject item = agents.getJSONObject(j);
-                                                if (item.getString("agent_name").equals("工时管家")) {
-                                                    agentId = item.getLong("agentid");
+                            //再处理其他的情况
+                            for (int i=0;i<bizData.size(); i++) {
+                                JSONObject bizItem = bizData.getJSONObject(i);
+                                int bizType = bizItem.getInteger("biz_type");
+                                if (bizType == 4) {
+                                    //企业授权变更,包含授权、解除授权、授权变更。
+                                    JSONObject biz_data = JSONObject.parseObject(bizItem.getString("biz_data"));
+                                    System.out.println("授权变更数据===@@");
+                                    System.out.println(biz_data);
+                                    String syncAction = biz_data.getString("syncAction");
+                                    if ("org_suite_auth".equals(syncAction) || "org_suite_change".equals(syncAction)) {
+                                        //开通授权应用
+                                        JSONObject authCorpInfo = biz_data.getJSONObject("auth_corp_info");
+                                        String corp_name = authCorpInfo.getString("corp_name");
+                                        boolean isAuthenticated = authCorpInfo.getBooleanValue("is_authenticated");
+                                        Long agentId = null;
+                                        if ("org_suite_auth".equals(syncAction)) {
+                                            System.out.println("==Push 推送事件 开通授权应用===" + corp_name);
+                                            //此处在授权开通时强制赋值
+                                            isAuthenticated = true;
+                                            JSONObject authInfo = biz_data.getJSONObject("auth_info");
+
+                                            if (authInfo != null) {
+                                                JSONArray agents = authInfo.getJSONArray("agent");
+                                                for (int j=0;j<agents.size(); j++) {
+                                                    JSONObject item = agents.getJSONObject(j);
+                                                    if (item.getString("agent_name").equals("工时管家")) {
+                                                        agentId = item.getLong("agentid");
+                                                    }
                                                 }
                                             }
+                                        } else {
+                                            System.out.println("==Push 推送事件 授权变更==="+ corp_name+"=="+(isAuthenticated?"启用":"停用"));
+                                        }
+                                        if (isAuthenticated) {
+                                            String corpid = authCorpInfo.getString("corpid");
+                                            String authUserId = biz_data.getJSONObject("auth_user_info").getString("userId");
+                                            try {
+                                                dingDingService.corpAuth(corpid, corp_name, authUserId, agentId);
+                                            } catch (ApiException e) {
+                                                e.printStackTrace();
+                                                System.out.println(e.getMessage());
+                                            }
+                                        } else {
+                                            System.out.println("===未授权启用===");
                                         }
+
                                     } else {
-                                        System.out.println("==Push 推送事件 授权变更==="+ corp_name+"=="+(isAuthenticated?"启用":"停用"));
+                                        System.out.println("==Push 推送授权类型 ==="+syncAction);
+
                                     }
-                                    if (isAuthenticated) {
-                                        String corpid = authCorpInfo.getString("corpid");
-                                        String authUserId = biz_data.getJSONObject("auth_user_info").getString("userId");
+                                } else if (bizType == 7) {
+                                    JSONObject biz_data = JSONObject.parseObject(bizItem.getString("biz_data"));
+                                    String syncAction = biz_data.getString("syncAction");
+                                    if ("org_micro_app_scope_update".equals(syncAction)) {
+                                        //授权范围变更,我们尝试重新获取组织结构
+                                        String corpid = bizItem.getString("corp_id");
+                                        System.out.println("==Push 推送事件 使用范围变更=调用syncCorpMembs, corpid==="+corpid);
                                         try {
-                                            dingDingService.corpAuth(corpid, corp_name, authUserId, agentId);
+                                            dingDingService.syncCorpMembs(corpid);
                                         } catch (ApiException e) {
                                             e.printStackTrace();
-                                            System.out.println(e.getMessage());
                                         }
-                                    } else {
-                                        System.out.println("===未授权启用===");
+                                    } else if ("org_micro_app_restore".equals(syncAction)) {
+                                        String corpid = bizItem.getString("corp_id");
+                                        System.out.println("==Push 推送事件 app 工时管家恢复启用, corpid==="+corpid);
                                     }
-
-                                } else {
-                                    System.out.println("==Push 推送授权类型 ==="+syncAction);
-
                                 }
-                            } else if (bizType == 7) {
-                                JSONObject biz_data = JSONObject.parseObject(bizItem.getString("biz_data"));
-                                String syncAction = biz_data.getString("syncAction");
-                                if ("org_micro_app_scope_update".equals(syncAction)) {
-                                    //授权范围变更,我们尝试重新获取组织结构
-                                    String corpid = bizItem.getString("corp_id");
-                                    System.out.println("==Push 推送事件 使用范围变更=调用syncCorpMembs, corpid==="+corpid);
-                                    try {
-                                        dingDingService.syncCorpMembs(corpid);
-                                    } catch (ApiException e) {
-                                        e.printStackTrace();
+                            }
+                        }
+                    } else if ("SYNC_HTTP_PUSH_MEDIUM".equals(eventType)) {
+                        JSONArray bizData = eventJson.getJSONArray("bizData");
+                        if (bizData.size() > 0) {
+                            //优先处理suite_ticket推送
+                            for (int i = 0; i < bizData.size(); i++) {
+                                JSONObject bizItem = bizData.getJSONObject(i);
+                                int bizType = bizItem.getInteger("biz_type");
+                                if (13 == bizType) {
+                                    JSONObject actionBizData = bizItem.getJSONObject("biz_data");
+                                    String syncAction = actionBizData.getString("syncAction");
+                                    if ("user_leave_org".equals(syncAction)) {
+                                        //员工离职
+                                        String corpId = bizItem.getString("corp_id");
+                                        String unionId = actionBizData.getString("unionid");
+                                        Integer companyId = companyDingdingMapper.selectById(corpId).getCompanyId();
+                                        User user = userMapper.selectOne(new QueryWrapper<User>().eq("company_id", companyId).eq("dingding_unionid", unionId));
+                                        if (user != null && user.getIsActive() == 1) {
+                                            user.setIsActive(0);
+                                            user.setInactiveDate(LocalDate.now());
+                                            userMapper.updateById(user);
+                                        }
                                     }
-                                } else if ("org_micro_app_restore".equals(syncAction)) {
-                                    String corpid = bizItem.getString("corp_id");
-                                    System.out.println("==Push 推送事件 app 工时管家恢复启用, corpid==="+corpid);
                                 }
                             }
                         }
@@ -313,7 +358,7 @@ public class DingDingController {
 
     @RequestMapping("/syncUserWorkData")
     public HttpRespMsg syncUserWorkData(Integer companyId, String startDate, String endDate) {
-        CompanyDingding dingding = companyDingdingMapper.selectOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId).isNotNull("custom_appkey"));
+        CompanyDingding dingding = companyDingdingMapper.selectOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId).isNotNull("inner_appkey"));
         HttpRespMsg msg = new HttpRespMsg();
         if (startDate.compareTo(endDate) > 0) {
             msg.setError("开始日期不能晚于结束日期");
@@ -322,7 +367,7 @@ public class DingDingController {
 
         if (companyId != null) {
             if (dingding == null) {
-                msg.setError("该公司没有设置customAppkey和customAppSecrt");
+                msg.setError("该公司没有设置innerAppkey和innerAppSecrt");
                 return msg;
             } else {
                 long t1 = System.currentTimeMillis();
@@ -335,10 +380,10 @@ public class DingDingController {
             List<Integer> compIds = timeTypeList.stream().map(TimeType::getCompanyId).collect(Collectors.toList());
             //企业内部应用才有权限调用
             List<CompanyDingding> dingdingList = companyDingdingMapper.selectList(new QueryWrapper<CompanyDingding>().in("company_id", compIds)
-                    .isNotNull("custom_appkey"));
+                    .isNotNull("inner_appkey"));
             System.out.println("==========获取钉钉内部应用的数量是:"+dingdingList.size());
             if (dingdingList.size() == 0) {
-                msg.setError("没有设置customAppkey和customAppSecrt的数据");
+                msg.setError("没有设置innerAppkey和innerAppSecrt的数据");
                 return msg;
             } else {
                 long t1 = System.currentTimeMillis();
@@ -375,4 +420,31 @@ public class DingDingController {
         companyDingdingService.sendBusinessTripSettingMsg(companyId, dingding.getAgentId(), "040534176023851922");
         return new HttpRespMsg();
     }
+
+    /**
+     * 初始化内部应用的系统数据
+     * @return
+     */
+    @RequestMapping("/initSystem")
+    public HttpRespMsg initSystem(String corpid) {
+        try {
+            return dingDingService.initSystem(corpid);
+        } catch (ApiException e) {
+            e.printStackTrace();
+            String errMsg = e.getErrMsg();
+            HttpRespMsg msg = new HttpRespMsg();
+            msg.setError(errMsg);
+            return msg;
+        }
+    }
+
+    @RequestMapping("/removeCompInfo")
+    public HttpRespMsg removeCompInfo(String corpid) {
+        return dingDingService.removeCompInfo(corpid);
+    }
+
+    @RequestMapping("/initSuperManager")
+    public HttpRespMsg initSuperManager(String corpid, String name) {
+        return dingDingService.initSuperManager(corpid, name);
+    }
 }

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

@@ -1,7 +1,6 @@
 package com.management.platform.controller;
 
 
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.entity.ExpenseSheet;
 import com.management.platform.entity.User;
 import com.management.platform.entity.vo.SysRichFunction;
@@ -10,9 +9,9 @@ import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.ExpenseSheetService;
 import com.management.platform.util.HttpRespMsg;
 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 javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
@@ -89,6 +88,9 @@ public class ExpenseSheetController {
         return expenseSheetService.deny(id);
 
     }
-
+    @RequestMapping("/importData")
+    public HttpRespMsg importData(HttpServletRequest request,MultipartFile[] files){
+        return expenseSheetService.importData(request,files);
+    }
 }
 

+ 8 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java

@@ -488,5 +488,13 @@ public class ProjectController {
     public HttpRespMsg getProjectByCustomer(Integer customerId,HttpServletRequest request){
         return projectService.getProjectByCustomer(customerId,request);
     }
+    @RequestMapping("/getUserReportTimelinessRate")
+    public HttpRespMsg getUserReportTimelinessRate(HttpServletRequest request,String startDate,String endDate,Integer departmentId,String userId,Integer pageIndex,Integer pageSize){
+        return projectService.getUserReportTimelinessRate(request,startDate,endDate,departmentId,userId,pageIndex,pageSize);
+    }
+    @RequestMapping("/exportReportTimelinessRate")
+    public HttpRespMsg exportReportTimelinessRate(HttpServletRequest request,String startDate,String endDate){
+        return projectService.exportReportTimelinessRate(request,startDate,endDate);
+    }
 }
 

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

@@ -204,5 +204,9 @@ public class ReportExtraDegreeController {
     public HttpRespMsg importData(HttpServletRequest request, MultipartFile file){
        return reportExtraDegreeService.importData(request,file);
     }
+    @RequestMapping("/exportData")
+    public HttpRespMsg exportData(HttpServletRequest request){
+        return reportExtraDegreeService.exportData(request);
+    }
 }
 

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

@@ -55,10 +55,17 @@ public class WeiXinCorpController {
     public static final String GET_CHECKINDATA = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckindata?access_token=ACCESS_TOKEN";
 
     public static final String GET_CHECKINOPTION = "https://qyapi.weixin.qq.com/cgi-bin/checkin/getcheckinoption?access_token=ACCESS_TOKEN";
+
+    public static final String GET_CORP_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET";
+
     @Value("${suitId}")
     private String suitId;
     @Value("${suitSecret}")
     private String suitSecret;
+
+    //自己公司的企业微信通讯录secret
+    private String concactSecret="irf7s8fy16wLhWGb8IxvGL2Nkm0sTB6dludpe_3VP6k";
+
     @Value("${token}")
     private String token;
     @Value("${encodingAesKey}")
@@ -175,6 +182,32 @@ public class WeiXinCorpController {
         return msg;
     }
 
+    //获取企业通讯录的accessToken
+    private String getCorpConcactAccessToken(WxCorpInfo corpInfo) throws Exception {
+        if (corpInfo.getExpireTime().isBefore(LocalDateTime.now())) {
+            String url = GET_CORP_TOKEN.replace("ID", corpInfo.getCorpid()).replace("SECRET", concactSecret);
+//            HttpHeaders headers = new HttpHeaders();
+//            headers.setContentType(MediaType.APPLICATION_JSON);
+//            JSONObject reqParam = new JSONObject();
+//            reqParam.put("auth_corpid",  corpInfo.getCorpid());
+//            reqParam.put("permanent_code",  corpInfo.getPermanentCode());
+////            HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+            ResponseEntity<String> responseEntity = this.restTemplate.exchange(url,
+                    HttpMethod.GET, null, String.class);
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                String resp = responseEntity.getBody();
+                JSONObject json = JSONObject.parseObject(resp);
+                if (json.getIntValue("errcode") == 0) {
+                    String access_token = json.getString("access_token");
+                    corpInfo.setAccessToken(access_token);
+                } else {
+                    throw new Exception(json.toJSONString());
+                }
+            }
+        }
+        return corpInfo.getAccessToken();
+    }
+
     //获取企业AccessToken
     private String getCorpAccessToken(WxCorpInfo corpInfo) throws Exception {
         if (corpInfo.getExpireTime().isBefore(LocalDateTime.now())) {
@@ -328,6 +361,23 @@ public class WeiXinCorpController {
                     param.setIndate(LocalDateTime.now());
                     sysConfigMapper.updateById(param);
                 }
+            } else if (jsonObject.has("ChangeType")) {
+                //{"xml":{"ChangeType":"delete_user","UserID":"ShanShuiGongZhangTianYiSe",
+                // "SuiteId":"ww4e237fd6abb635af","InfoType":"change_contact","AuthCorpId":"wwf11426cf618e1703",
+                // "TimeStamp":1655908762,"OpenUserID":"woy9TkCAAApdqSxsfJbmK4cBJhbzI5Ug"}}
+                String changeType = jsonObject.getString("ChangeType");
+                if ("delete_user".equals(changeType)) {
+                    //监听到员工离职
+                    String corpId = jsonObject.getString("AuthCorpId");
+                    String corpWxUserId = jsonObject.getString("UserID");
+                    Integer companyId = wxCorpInfoMapper.selectById(corpId).getCompanyId();
+                    User user = userMapper.selectOne(new QueryWrapper<User>().eq("corpwx_userid", corpWxUserId).eq("company_id", companyId));
+                    if (user.getIsActive() == 1) {
+                        user.setIsActive(0);
+                        user.setInactiveDate(LocalDate.now());
+                        userMapper.updateById(user);
+                    }
+                }
             }
         } catch (Exception e) {
             // TODO
@@ -931,7 +981,7 @@ public class WeiXinCorpController {
         Company company = companyMapper.selectById(wxCorpInfo.getCompanyId());
         String curCorpAccessToken = null;
         try {
-            curCorpAccessToken = getCorpAccessToken(wxCorpInfo);
+            curCorpAccessToken = getCorpConcactAccessToken(wxCorpInfo);
         } catch (Exception exception) {
             exception.printStackTrace();
         }

+ 5 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Company.java

@@ -1,15 +1,16 @@
 package com.management.platform.entity;
 
 import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.extension.activerecord.Model;
-import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
 import com.baomidou.mybatisplus.annotation.TableField;
-import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
 /**
  * <p>
  * 

+ 16 - 9
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/CompanyDingding.java

@@ -1,21 +1,22 @@
 package com.management.platform.entity;
 
-import com.baomidou.mybatisplus.extension.activerecord.Model;
-import com.baomidou.mybatisplus.annotation.TableId;
-import java.time.LocalDateTime;
 import com.baomidou.mybatisplus.annotation.TableField;
-import java.io.Serializable;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
 
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
 /**
  * <p>
  * 
  * </p>
  *
  * @author Seyason
- * @since 2022-04-29
+ * @since 2022-06-22
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -63,14 +64,14 @@ public class CompanyDingding extends Model<CompanyDingding> {
     /**
      * 企业内部应用appkey
      */
-    @TableField("custom_appkey")
-    private String customAppkey;
+    @TableField("inner_appkey")
+    private String innerAppkey;
 
     /**
      * 企业内部应用appsecret
      */
-    @TableField("custom_appsecret")
-    private String customAppsecret;
+    @TableField("inner_appsecret")
+    private String innerAppsecret;
 
     /**
      * 企业内部应用token
@@ -84,6 +85,12 @@ public class CompanyDingding extends Model<CompanyDingding> {
     @TableField("inner_expire_time")
     private LocalDateTime innerExpireTime;
 
+    /**
+     * 企业链接地址
+     */
+    @TableField("web_url")
+    private String webUrl;
+
 
     @Override
     protected Serializable pkVal() {

+ 10 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java

@@ -17,7 +17,7 @@ import java.util.List;
  * </p>
  *
  * @author Seyason
- * @since 2022-06-09
+ * @since 2022-06-23
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -110,9 +110,8 @@ public class TimeType extends Model<TimeType> {
     @TableField("custom_degree_name")
     private String customDegreeName;
 
-
     /**
-     * 工时填报自定义维度是否必填
+     * 工时填报自定义维度是否必填 0-否 1-是
      */
     @TableField("custom_degree_status")
     private Integer customDegreeStatus;
@@ -153,9 +152,8 @@ public class TimeType extends Model<TimeType> {
     @TableField("custom_data_name")
     private String customDataName;
 
-
     /**
-     * 工时填报数值收集字段是否必填
+     * 工时填报数值收集字段是否必填 0-否 1-是
      */
     @TableField("custom_data_status")
     private Integer customDataStatus;
@@ -250,8 +248,15 @@ public class TimeType extends Model<TimeType> {
     @TableField("alert_type")
     private Integer alertType;
 
+    /**
+     * 0-工作内容非必填 1-工作内容必填
+     */
+    @TableField("work_content_state")
+    private Integer workContentState;
+
     @TableField(exist = false)
     private List<User> userList;
+
     @Override
     protected Serializable pkVal() {
         return this.companyId;

+ 14 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/TimelinessRateVO.java

@@ -0,0 +1,14 @@
+package com.management.platform.entity.vo;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+@EqualsAndHashCode(callSuper = false)
+public class TimelinessRateVO {
+    private String userName;
+    private String departmentName;
+    private String timelinessRate;
+}

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

@@ -55,7 +55,7 @@ public interface ReportMapper extends BaseMapper<Report> {
                                                  @Param("isEngeering") Integer isEngeering,
                                                  @Param("startDate") String startDate,
                                                  @Param("endDate") String endDate,
-                                                 @Param("targetUserId") String targetUserId
+                                                 @Param("targetUserId") List<String> targetUserId
                                                  );
 
     //获取本人负责的专业的相关的日报
@@ -129,4 +129,6 @@ public interface ReportMapper extends BaseMapper<Report> {
 
     @Update("update report set task_id = null where task_id=#{taskId}")
     void deleteReportTask(Integer taskId);
+
+    List<Map<String, Object>> getUserReportTimelinessRate(Integer companyId,String startDate, String endDate);
 }

+ 6 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/CompanyDingdingService.java

@@ -1,10 +1,10 @@
 package com.management.platform.service;
 
-import com.management.platform.entity.CompanyDingding;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.management.platform.entity.WxCorpInfo;
+import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
+import com.management.platform.entity.CompanyDingding;
 
-import java.util.HashMap;
+import java.util.List;
 
 /**
  * <p>
@@ -31,5 +31,7 @@ public interface CompanyDingdingService extends IService<CompanyDingding> {
 
     public void sendFinishMileStoneMsg(CompanyDingding dingding, String useridList, String taskName, Integer projectId, String project, String finishDate);
 
-    public void sendOAMsg(CompanyDingding dingding, String useridList, HashMap map);
+    public void sendOAMsg(CompanyDingding dingding, String useridList,String titleText,String content, List<OapiMessageCorpconversationAsyncsendV2Request.Form> form,Integer projectId);
+
+    public void sendInnerLinkMsg(CompanyDingding dingding, String useridList, String title, String alertMsg);
 }

+ 2 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/CompanyService.java

@@ -2,6 +2,7 @@ package com.management.platform.service;
 
 import com.management.platform.entity.Company;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.util.HttpRespMsg;
 
 /**
  * <p>
@@ -12,5 +13,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  * @since 2020-01-13
  */
 public interface CompanyService extends IService<Company> {
-
+    HttpRespMsg dataMigration(Integer oldCompanyId, Integer targetCompanyId);
 }

+ 13 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/DingDingService.java

@@ -1,10 +1,15 @@
 package com.management.platform.service;
 
+import com.alibaba.fastjson.JSONArray;
 import com.management.platform.entity.CompanyDingding;
 import com.management.platform.util.HttpRespMsg;
 import com.taobao.api.ApiException;
 
+import java.time.LocalDate;
+
 public interface DingDingService {
+    public HttpRespMsg initSystem(String corpid) throws ApiException;
+
     public void corpAuth(String corpid, String corpName, String authUserId, Long agentId) throws ApiException;
 
     public String syncCorpMembs(String corpid) throws ApiException;
@@ -35,4 +40,12 @@ public interface DingDingService {
 //    public HttpRespMsg getLeaveTypeList(String leaveCode, Integer companyId, Integer departmentId, String userId, Integer pageIndex);
 
     public HttpRespMsg syncLeaveQuotaData(Integer companyId);
+
+    HttpRespMsg removeCompInfo(String corpid);
+
+    HttpRespMsg initSuperManager(String corpid, String name);
+
+    void userLeaveOrg(String corpId, JSONArray userIdArray, LocalDate leaveDate);
+
+    void userAddOrg(String corpId, JSONArray userIdArray);
 }

+ 6 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExpenseSheetService.java

@@ -1,9 +1,11 @@
 package com.management.platform.service;
 
-import com.management.platform.entity.ExpenseSheet;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.entity.ExpenseSheet;
 import com.management.platform.util.HttpRespMsg;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
 
 /**
  * <p>
@@ -28,4 +30,6 @@ public interface ExpenseSheetService extends IService<ExpenseSheet> {
     HttpRespMsg approve(Integer id);
 
     HttpRespMsg deny(Integer id);
+
+    HttpRespMsg importData(HttpServletRequest request, MultipartFile[] files);
 }

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java

@@ -135,4 +135,8 @@ public interface ProjectService extends IService<Project> {
     HttpRespMsg getUserWorkingTimeList(String userId, Integer projectId, String startDate, String endDate, Integer pageIndex, Integer pageSize,HttpServletRequest request);
 
     HttpRespMsg exportUserWorkingTimeList(String userId, Integer projectId, String startDate, String endDate,HttpServletRequest request);
+
+    HttpRespMsg getUserReportTimelinessRate(HttpServletRequest request, String startDate, String endDate, Integer departmentId, String userId,Integer pageIndex,Integer pageSize);
+
+    HttpRespMsg exportReportTimelinessRate(HttpServletRequest request, String startDate, String endDate);
 }

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

@@ -18,4 +18,6 @@ import javax.servlet.http.HttpServletRequest;
 public interface ReportExtraDegreeService extends IService<ReportExtraDegree> {
 
     HttpRespMsg importData(HttpServletRequest request, MultipartFile file);
+
+    HttpRespMsg exportData(HttpServletRequest request);
 }

+ 298 - 155
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/CompanyDingdingServiceImpl.java

@@ -6,12 +6,14 @@ import com.aliyun.dingtalkoauth2_1_0.models.GetCorpAccessTokenResponse;
 import com.aliyun.tea.TeaException;
 import com.aliyun.teaopenapi.models.Config;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.dingtalk.api.DefaultDingTalkClient;
 import com.dingtalk.api.DingTalkClient;
 import com.dingtalk.api.request.OapiGettokenRequest;
 import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
 import com.dingtalk.api.request.OapiMessageCorpconversationSendbytemplateRequest;
 import com.dingtalk.api.request.OapiServiceGetSuiteTokenRequest;
+import com.dingtalk.api.response.OapiGettokenResponse;
 import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
 import com.dingtalk.api.response.OapiMessageCorpconversationSendbytemplateResponse;
 import com.dingtalk.api.response.OapiServiceGetSuiteTokenResponse;
@@ -20,14 +22,16 @@ import com.management.platform.entity.SysConfig;
 import com.management.platform.mapper.CompanyDingdingMapper;
 import com.management.platform.mapper.SysConfigMapper;
 import com.management.platform.service.CompanyDingdingService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.taobao.api.ApiException;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
+import java.net.URLEncoder;
 import java.time.LocalDateTime;
-import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -58,203 +62,288 @@ public class CompanyDingdingServiceImpl extends ServiceImpl<CompanyDingdingMappe
 
     @Resource
     private SysConfigMapper sysConfigMapper;
+    @Resource
+    private CompanyDingdingMapper companyDingdingMapper;
     @Value("${dingding.appId}")
     private long appId;//钉钉第三方应用的appId
+    @Value("${configEnv.isPrivateDeploy}")
+    private boolean isPrivateDeploy;//是否是私有化部署,企业内部应用
+
 
     @Override
     public void sendFillReportAlertMsg(Integer companyId, Long agentId, String msg, String useridList) {
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-        OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
-        System.out.println("发送AlertMsg=="+msg+", agentId="+agentId);
         CompanyDingding dingding = getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
-        req.setAgentId(agentId);
+        if (isPrivateDeploy) {
+            sendInnerLinkMsg(dingding,useridList,"日报漏填提醒",msg);
+            List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+            OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item.setKey("说明:");
+            item.setValue(msg);
+            form.add(item);
+            sendOAMsg(dingding,useridList,"日报漏填提醒","您有日报漏填,请及时填报",form,null);
+        } else {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+            OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+            System.out.println("发送AlertMsg=="+msg+", agentId="+agentId);
+            req.setAgentId(agentId);
 //        req.setDeptIdList("421897262");
-        req.setUseridList(useridList);
-        req.setTemplateId(TEMPLATE_ALERT_REPORT);
-        JSONObject json = new JSONObject();
-        json.put("APPID", ""+appId);
-        json.put("CORPID", dingding.getCorpid());
-        json.put("msg", msg);
-        req.setData(json.toJSONString());
-        OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-        try {
-            rsp = client.execute(req, getCorpAccessToken(dingding));
-        } catch (ApiException e) {
-            e.printStackTrace();
-        } catch (Exception exception) {
-            exception.printStackTrace();
+            req.setUseridList(useridList);
+            req.setTemplateId(TEMPLATE_ALERT_REPORT);
+            JSONObject json = new JSONObject();
+            json.put("APPID", ""+appId);
+            json.put("CORPID", dingding.getCorpid());
+            json.put("msg", msg);
+            req.setData(json.toJSONString());
+            OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+            try {
+                rsp = client.execute(req, getCorpAccessToken(dingding));
+            } catch (ApiException e) {
+                e.printStackTrace();
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            System.out.println(rsp.getBody());
         }
-        System.out.println(rsp.getBody());
     }
 
     @Override
     public void sendRejectReportMsg(Integer companyId, String date, String projectNames, String reason, String auditorName, String useridList) {
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-        OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
         CompanyDingding dingding = getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
-        req.setAgentId(dingding.getAgentId());
-        req.setUseridList(useridList);
-        req.setTemplateId(TEMPLATE_REJECT_REPORT);
-        JSONObject json = new JSONObject();
-        json.put("createDate", date);
-        json.put("project", projectNames);
-        json.put("reason", reason);
-        json.put("auditUser", auditorName);
-        json.put("APPID", ""+appId);
-        json.put("CORPID", dingding.getCorpid());
-        req.setData(json.toJSONString());
-        OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-        try {
-            rsp = client.execute(req, getCorpAccessToken(dingding));
-        } catch (ApiException e) {
-            e.printStackTrace();
-        } catch (Exception exception) {
-            exception.printStackTrace();
+        if (isPrivateDeploy) {
+            List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+            OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item.setKey("日报日期:");
+            item.setValue(date);
+            form.add(item);
+            OapiMessageCorpconversationAsyncsendV2Request.Form item1=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item1.setKey("所属项目:");
+            item1.setValue(projectNames);
+            form.add(item1);
+            OapiMessageCorpconversationAsyncsendV2Request.Form item2=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item2.setKey("驳回原因:");
+            item2.setValue(reason);
+            form.add(item2);
+            OapiMessageCorpconversationAsyncsendV2Request.Form item3=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item3.setKey("审批人:");
+            item3.setValue(auditorName);
+            form.add(item3);
+            sendOAMsg(dingding,useridList,"日报驳回提醒","您的日报被驳回,请及时查看",form,null);
+        } else {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+            OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+            req.setAgentId(dingding.getAgentId());
+            req.setUseridList(useridList);
+            req.setTemplateId(TEMPLATE_REJECT_REPORT);
+            JSONObject json = new JSONObject();
+            json.put("createDate", date);
+            json.put("project", projectNames);
+            json.put("reason", reason);
+            json.put("auditUser", auditorName);
+            json.put("APPID", "" + appId);
+            json.put("CORPID", dingding.getCorpid());
+            req.setData(json.toJSONString());
+            OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+            try {
+                rsp = client.execute(req, getCorpAccessToken(dingding));
+            } catch (ApiException e) {
+                e.printStackTrace();
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            System.out.println(rsp.getBody());
         }
-        System.out.println(rsp.getBody());
     }
-
     @Override
     public void sendLeaveApplyAlertMsg(Integer companyId, Long agentId, String msg, String useridList) {
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-        OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
-        System.out.println("发送 请假提交审批 =="+msg+", agentId="+agentId);
         CompanyDingding dingding = getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
-        req.setAgentId(agentId);
+        if (isPrivateDeploy) {
+            sendInnerLinkMsg(dingding,useridList,"请假提交审批提醒",msg);
+            List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+            OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item.setKey("说明:");
+            item.setValue(msg);
+            form.add(item);
+            sendOAMsg(dingding,useridList,"请假提交审批提醒","您的请假还未提交审批,请及时提交",form,null);
+        } else {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+            OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+            System.out.println("发送 请假提交审批 ==" + msg + ", agentId=" + agentId);
+
+            req.setAgentId(agentId);
 //        req.setDeptIdList("421897262");
-        req.setUseridList(useridList);
-        req.setTemplateId(TEMPLATE_LEAVE_APPLY);
-        JSONObject json = new JSONObject();
-        json.put("employeeName", "张辉");
-        json.put("deptName", "质量部");
-        json.put("date", "2022-03-29");
-        json.put("APPID", ""+appId);
-        json.put("CORPID", dingding.getCorpid());
-        req.setData(json.toJSONString());
-        OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-        try {
-            rsp = client.execute(req, getCorpAccessToken(dingding));
-        } catch (ApiException e) {
-            e.printStackTrace();
-        } catch (Exception exception) {
-            exception.printStackTrace();
+            req.setUseridList(useridList);
+            req.setTemplateId(TEMPLATE_LEAVE_APPLY);
+            JSONObject json = new JSONObject();
+            json.put("employeeName", "张辉");
+            json.put("deptName", "质量部");
+            json.put("date", "2022-03-29");
+            json.put("APPID", "" + appId);
+            json.put("CORPID", dingding.getCorpid());
+            req.setData(json.toJSONString());
+            OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+            try {
+                rsp = client.execute(req, getCorpAccessToken(dingding));
+            } catch (ApiException e) {
+                e.printStackTrace();
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            System.out.println(rsp.getBody());
         }
-        System.out.println(rsp.getBody());
     }
-
     @Override
     public void sendReportWaitingApplyMsg(Integer companyId, Long agentId, Long auditNum, String useridList) {
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-        OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
-        System.out.println("发送 待审批日报数量 =="+auditNum+", agentId="+agentId);
         CompanyDingding dingding = getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
-        req.setAgentId(agentId);
-        req.setUseridList(useridList);
-        req.setTemplateId(TEMPLATE_REPORT_WAITING_APPLY);
-        JSONObject json = new JSONObject();
-        json.put("auditNum", ""+auditNum);
-        json.put("APPID", ""+appId);
-        json.put("CORPID", dingding.getCorpid());
-        req.setData(json.toJSONString());
-        OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-        try {
-            rsp = client.execute(req, getCorpAccessToken(dingding));
-        } catch (ApiException e) {
-            e.printStackTrace();
-        } catch (Exception exception) {
-            exception.printStackTrace();
+        if (isPrivateDeploy) {
+            List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+            OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item.setKey("待审核数量:");
+            item.setValue(String.valueOf(auditNum));
+            form.add(item);
+            sendOAMsg(dingding,useridList,"您还有日报暂未审核","请及时审核",form,null);
+        } else {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+            OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+            System.out.println("发送 待审批日报数量 ==" + auditNum + ", agentId=" + agentId);
+            req.setAgentId(agentId);
+            req.setUseridList(useridList);
+            req.setTemplateId(TEMPLATE_REPORT_WAITING_APPLY);
+            JSONObject json = new JSONObject();
+            json.put("auditNum", "" + auditNum);
+            json.put("APPID", "" + appId);
+            json.put("CORPID", dingding.getCorpid());
+            req.setData(json.toJSONString());
+            OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+            try {
+                rsp = client.execute(req, getCorpAccessToken(dingding));
+            } catch (ApiException e) {
+                e.printStackTrace();
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            System.out.println(rsp.getBody());
         }
-        System.out.println(rsp.getBody());
     }
-
     @Override
     public void sendBusinessTripSettingMsg(Integer companyId, Long agentId, String useridList) {
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-        OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
         CompanyDingding dingding = getOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
-        req.setAgentId(agentId);
-        req.setUseridList(useridList);
-        req.setTemplateId(TEMPLATE_BUSTRIP_SETTING);
-        JSONObject json = new JSONObject();
-        json.put("APPID", ""+appId);
-        json.put("CORPID", dingding.getCorpid());
-        req.setData(json.toJSONString());
-        OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-        try {
-            rsp = client.execute(req, getCorpAccessToken(dingding));
-        } catch (ApiException e) {
-            e.printStackTrace();
-        } catch (Exception exception) {
-            exception.printStackTrace();
+        if (isPrivateDeploy) {
+            sendInnerLinkMsg(dingding,useridList,"关联出差提醒","您还有出差记录尚未关联到项目");
+        } else {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+            OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+            req.setAgentId(agentId);
+            req.setUseridList(useridList);
+            req.setTemplateId(TEMPLATE_BUSTRIP_SETTING);
+            JSONObject json = new JSONObject();
+            json.put("APPID", "" + appId);
+            json.put("CORPID", dingding.getCorpid());
+            req.setData(json.toJSONString());
+            OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+            try {
+                rsp = client.execute(req, getCorpAccessToken(dingding));
+            } catch (ApiException e) {
+                e.printStackTrace();
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            System.out.println(rsp.getBody());
         }
-        System.out.println(rsp.getBody());
     }
-
     @Override
     public void sendNewTaskMsg(CompanyDingding dingding, String ddUserid, String title, String endDate) {
-        //异步发送
-        new Thread(){
-            public void run() {
-                DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-                OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
-                req.setAgentId(dingding.getAgentId());
-                req.setUseridList(ddUserid);
-                req.setTemplateId(TEMPLATE_NEW_TASK);
-                JSONObject json = new JSONObject();
-                json.put("name", ""+title);
-                json.put("endDate", ""+endDate);
-                json.put("APPID", ""+appId);
-                json.put("CORPID", dingding.getCorpid());
-                req.setData(json.toJSONString());
+        if (isPrivateDeploy) {
+            List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+            OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item.setKey("任务标题:");
+            item.setValue(title);
+            form.add(item);
+            OapiMessageCorpconversationAsyncsendV2Request.Form item1=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item1.setKey("截至日期:");
+            item1.setValue(endDate);
+            form.add(item1);
+            sendOAMsg(dingding,ddUserid,"新任务提醒","您有一条新的任务待执行,请关注",form,null);
+        } else {
+            //异步发送
+            new Thread() {
+                public void run() {
+                    DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+                    OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+                    req.setAgentId(dingding.getAgentId());
+                    req.setUseridList(ddUserid);
+                    req.setTemplateId(TEMPLATE_NEW_TASK);
+                    JSONObject json = new JSONObject();
+                    json.put("name", "" + title);
+                    json.put("endDate", "" + endDate);
+                    json.put("APPID", "" + appId);
+                    json.put("CORPID", dingding.getCorpid());
+                    req.setData(json.toJSONString());
 
-                OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-                try {
-                    rsp = client.execute(req, getCorpAccessToken(dingding));
-                } catch (ApiException e) {
-                    e.printStackTrace();
-                } catch (Exception exception) {
-                    exception.printStackTrace();
+                    OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+                    try {
+                        rsp = client.execute(req, getCorpAccessToken(dingding));
+                    } catch (ApiException e) {
+                        e.printStackTrace();
+                    } catch (Exception exception) {
+                        exception.printStackTrace();
+                    }
+                    System.out.println(rsp.getBody());
                 }
-                System.out.println(rsp.getBody());
-            }
-        }.start();
+            }.start();
+        }
     }
 
-
     @Override
     public void sendFinishMileStoneMsg(CompanyDingding dingding, String useridList, String taskName, Integer projectId, String project, String finishDate) {
-        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
-        OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
-        req.setAgentId(dingding.getAgentId());
-        req.setUseridList(useridList);
-        req.setTemplateId(TEMPLATE_FINISH_MILESTONE);
-        JSONObject json = new JSONObject();
-        json.put("taskName", taskName);
-        json.put("project", project);
-        json.put("PROJECTID", ""+projectId);
-        json.put("APPID", ""+appId);
-        json.put("CORPID", dingding.getCorpid());
-        json.put("finishDate", finishDate);
-        req.setData(json.toJSONString());
-        OapiMessageCorpconversationSendbytemplateResponse rsp = null;
-        try {
-            rsp = client.execute(req, getCorpAccessToken(dingding));
-        } catch (ApiException e) {
-            e.printStackTrace();
-        } catch (Exception exception) {
-            exception.printStackTrace();
+        if (isPrivateDeploy) {
+            List<OapiMessageCorpconversationAsyncsendV2Request.Form> form=new ArrayList<>();
+            OapiMessageCorpconversationAsyncsendV2Request.Form item=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item.setKey("里程碑:");
+            item.setValue(taskName);
+            form.add(item);
+            OapiMessageCorpconversationAsyncsendV2Request.Form item1=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item1.setKey("所属项目:");
+            item1.setValue(project);
+            form.add(item1);
+            OapiMessageCorpconversationAsyncsendV2Request.Form item2=new OapiMessageCorpconversationAsyncsendV2Request.Form();
+            item2.setKey("完成时间:");
+            item2.setValue(finishDate);
+            form.add(item2);
+            sendOAMsg(dingding,useridList,"里程碑完成提醒","我们刚刚完成了以下里程碑,感谢大家的努力!",form,projectId);
+        } else {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/sendbytemplate");
+            OapiMessageCorpconversationSendbytemplateRequest req = new OapiMessageCorpconversationSendbytemplateRequest();
+            req.setAgentId(dingding.getAgentId());
+            req.setUseridList(useridList);
+            req.setTemplateId(TEMPLATE_FINISH_MILESTONE);
+            JSONObject json = new JSONObject();
+            json.put("taskName", taskName);
+            json.put("project", project);
+            json.put("PROJECTID", "" + projectId);
+            json.put("APPID", "" + appId);
+            json.put("CORPID", dingding.getCorpid());
+            json.put("finishDate", finishDate);
+            req.setData(json.toJSONString());
+            OapiMessageCorpconversationSendbytemplateResponse rsp = null;
+            try {
+                rsp = client.execute(req, getCorpAccessToken(dingding));
+            } catch (ApiException e) {
+                e.printStackTrace();
+            } catch (Exception exception) {
+                exception.printStackTrace();
+            }
+            System.out.println(rsp.getBody());
         }
-        System.out.println(rsp.getBody());
     }
 
+
     @Override
-    public void sendOAMsg(CompanyDingding dingding, String useridList, HashMap map) {
+    public void sendOAMsg(CompanyDingding dingding, String useridList,String titleText,String content,List<OapiMessageCorpconversationAsyncsendV2Request.Form> form,Integer projectId) {
         DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
         OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
         request.setAgentId(dingding.getAgentId());
         request.setUseridList(useridList);
         request.setToAllUser(false);
-
         OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
 //        msg.setMsgtype("text");
 //        msg.setText(new OapiMessageCorpconversationAsyncsendV2Request.Text());
@@ -287,11 +376,19 @@ public class CompanyDingdingServiceImpl extends ServiceImpl<CompanyDingdingMappe
 
         msg.setOa(new OapiMessageCorpconversationAsyncsendV2Request.OA());
         msg.getOa().setHead(new OapiMessageCorpconversationAsyncsendV2Request.Head());
-        msg.getOa().getHead().setText("这里是标题");
+        msg.getOa().getHead().setText(titleText);
         msg.getOa().setBody(new OapiMessageCorpconversationAsyncsendV2Request.Body());
-        msg.getOa().getBody().setContent("这里是内容");
+        msg.getOa().getBody().setContent(content);
+        msg.getOa().getBody().setForm(form);
         msg.setMsgtype("oa");
-        msg.getOa().setMessageUrl("dingtalk://dingtalkclient/action/openapp?corpid=="+dingding.getCorpid()+"&container_type=work_platform&app_id="+appId+"&redirect_type=jump&redirect_url=https%3A%2F%2Fworktime.ttkuaiban.com%2F%3Fcorpid%3D%24CORPID%24");
+        List<String> collect = form.stream().map(f -> f.getKey()).collect(Collectors.toList());
+        if(collect.contains("里程碑:")){
+            msg.getOa().setMessageUrl("dingtalk://dingtalkclient/action/openapp?corpid="+dingding.getCorpid()
+                    +"&container_type=work_platform&app_id=0_"+dingding.getAgentId()+"&redirect_type=jump&redirect_url="+ URLEncoder.encode(dingding.getWebUrl()+"?corpid="+dingding.getCorpid())+"&jumpto=/info/"+projectId);
+        }else {
+            msg.getOa().setMessageUrl("dingtalk://dingtalkclient/action/openapp?corpid="+dingding.getCorpid()
+                    +"&container_type=work_platform&app_id=0_"+dingding.getAgentId()+"&redirect_type=jump&redirect_url="+ URLEncoder.encode(dingding.getWebUrl()+"?corpid="+dingding.getCorpid()));
+        }
         request.setMsg(msg);
 
 //        msg.setActionCard(new OapiMessageCorpconversationAsyncsendV2Request.ActionCard());
@@ -303,13 +400,59 @@ public class CompanyDingdingServiceImpl extends ServiceImpl<CompanyDingdingMappe
 //        request.setMsg(msg);
         OapiMessageCorpconversationAsyncsendV2Response rsp = null;
         try {
-            rsp = client.execute(request, getCorpAccessToken(dingding));
+            rsp = client.execute(request, getInnerCorpToken(dingding));
         } catch (Exception exception) {
             exception.printStackTrace();
         }
         System.out.println(rsp.getBody());
     }
 
+    @Override
+    public void sendInnerLinkMsg(CompanyDingding dingding, String useridList, String title, String alertMsg) {
+        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/message/corpconversation/asyncsend_v2");
+        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
+        request.setAgentId(dingding.getAgentId());
+        request.setUseridList(useridList);
+        request.setToAllUser(false);
+
+        OapiMessageCorpconversationAsyncsendV2Request.Msg msg = new OapiMessageCorpconversationAsyncsendV2Request.Msg();
+
+        msg.setMsgtype("link");
+        msg.setLink(new OapiMessageCorpconversationAsyncsendV2Request.Link());
+        msg.getLink().setTitle(title);
+        msg.getLink().setText(alertMsg);
+        msg.getLink().setMessageUrl("dingtalk://dingtalkclient/action/openapp?corpid="+dingding.getCorpid()
+                +"&container_type=work_platform&app_id=0_"+dingding.getAgentId()+"&redirect_type=jump&redirect_url="+ URLEncoder.encode(dingding.getWebUrl()+"?corpid="+dingding.getCorpid()));
+        request.setMsg(msg);
+
+        OapiMessageCorpconversationAsyncsendV2Response rsp = null;
+        try {
+            rsp = client.execute(request, getInnerCorpToken(dingding));
+        } catch (Exception exception) {
+            exception.printStackTrace();
+        }
+        System.out.println(rsp.getBody());
+    }
+    /**
+     * 获取钉钉内部企业的token
+     */
+    private String getInnerCorpToken(CompanyDingding dingding) throws ApiException {
+        if (dingding.getInnerToken() == null || LocalDateTime.now().isAfter(dingding.getInnerExpireTime())) {
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+            OapiGettokenRequest request = new OapiGettokenRequest();
+            request.setAppkey(dingding.getInnerAppkey());
+            request.setAppsecret(dingding.getInnerAppsecret());
+            request.setHttpMethod("GET");
+            OapiGettokenResponse response = client.execute(request);
+            if (!response.isSuccess()) {
+                System.err.println("获取企业内部token失败:"+response.getErrorCode()+":"+response.getErrmsg());
+            }
+            dingding.setInnerToken(response.getAccessToken());
+            dingding.setInnerExpireTime(LocalDateTime.now().plusSeconds(response.getExpiresIn()));
+            companyDingdingMapper.updateById(dingding);
+        }
+        return dingding.getInnerToken();
+    }
     private String getDDSuiteAccessToken() throws ApiException {
         if (DingDingServiceImpl.SUITE_ACCESS_TOKEN == null || DingDingServiceImpl.suiteTokenExpireTime < System.currentTimeMillis()) {
             DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/service/get_suite_token");

+ 173 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/CompanyServiceImpl.java

@@ -1,11 +1,20 @@
 package com.management.platform.service.impl;
 
-import com.management.platform.entity.Company;
-import com.management.platform.mapper.CompanyMapper;
-import com.management.platform.service.CompanyService;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.*;
+import com.management.platform.service.*;
+import com.management.platform.util.HttpRespMsg;
+import com.mysql.cj.util.StringUtils;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
 /**
  * <p>
  *  服务实现类
@@ -16,5 +25,165 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> implements CompanyService {
-
+    @Resource
+    private CompanyMapper companyMapper;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private ProjectMapper projectMapper;
+    @Resource
+    private ProjectService projectService;
+    @Resource
+    private ReportMapper reportMapper;
+    @Resource
+    private CustomerInfoMapper customerInfoMapper;
+    @Resource
+    private ParticipationMapper participationMapper;
+    @Resource
+    private ParticipationService participationService;
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Resource
+    private DepartmentService departmentService;
+    @Resource
+    private ProjectAuditorMapper projectAuditorMapper;
+    @Resource
+    private ProjectAuditorService projectAuditorService;
+    @Resource
+    private UserService userService;
+    @Resource
+    private ReportService reportService;
+    @Override
+    public HttpRespMsg dataMigration(Integer oldCompanyId,Integer targetCompanyId) {
+        HttpRespMsg msg=new HttpRespMsg();
+        List<User> oldUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", oldCompanyId));
+        List<User> targetUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", targetCompanyId));
+        List<Department> oldDepartmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", oldCompanyId));
+        List<Department> targetDepartmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", targetCompanyId));
+        //处理部门负责人
+        List<User> updateUserList=new ArrayList<>();
+        List<Department> updateDepartmentList=new ArrayList<>();
+        for (Department oldDepartment : oldDepartmentList) {
+            for (Department targetDepartment : targetDepartmentList) {
+                if(oldDepartment.getDepartmentName().equals(targetDepartment.getDepartmentName())){
+                    if(oldDepartment.getManagerId()!=null){
+                        Optional<User> oldUser = oldUserList.stream().filter(ou -> ou.getId().equals(oldDepartment.getManagerId())).findFirst();
+                        Optional<User> targetUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(oldUser.get().getDingdingUserid())).findFirst();
+                        targetDepartment.setManagerId(targetUser.get().getId());
+                        targetUser.get().setDepartmentId(targetDepartment.getDepartmentId());
+                        targetUser.get().setDepartmentName(targetDepartment.getDepartmentName());
+                        updateUserList.add(targetUser.get());
+                        updateDepartmentList.add(targetDepartment);
+                    }
+                }
+            }
+        }
+        userService.updateBatchById(updateUserList);
+        departmentService.updateBatchById(updateDepartmentList);
+        List<String> ids=new ArrayList<>();
+        //获取在目标公司也存在的人员
+        for (User targetUser : targetUserList) {
+            for (User oldUser : oldUserList) {
+                if(targetUser.getDingdingUserid().equals(oldUser.getDingdingUserid())){
+                    ids.add(oldUser.getId());
+                }
+            }
+        }
+        List<Participation> participationList = participationMapper.selectList(new QueryWrapper<Participation>().in("user_id", ids));
+        //取项目id  按照项目参与人
+        List<Integer> projectIds = participationList.stream().map(pc -> pc.getProjectId()).collect(Collectors.toList());
+        List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", projectIds));
+        List<Project> targetProjectList = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", targetCompanyId));
+        List<String> list = targetProjectList.stream().map(tp -> tp.getProjectCode()).collect(Collectors.toList());
+        List<ProjectAuditor> oldProjectAuditorList = projectAuditorMapper.selectList(new QueryWrapper<ProjectAuditor>().in("project_id", projectIds));
+        //处理相关项目
+        List<Project> newProjectList=new ArrayList<>();
+        List<ProjectAuditor> newProjectAuditorList=new ArrayList<>();
+        List<Participation> newParticipationList=new ArrayList<>();
+        for (Project pro: projectList) {
+            //此前的项目负责人不存在当前组织架构
+            Optional<User> inchargerUser = oldUserList.stream().filter(ou -> ou.getId().equals(pro.getInchargerId())).findFirst();
+            boolean flag = targetUserList.stream().anyMatch(tu -> tu.getDingdingUserid().equals(inchargerUser.get().getDingdingUserid()));
+            if(!flag){
+                msg.setError("当前项目["+pro.getProjectName()+"]负责人["+inchargerUser.get().getName()+"]不存在");
+                return msg;
+            }else {
+                Optional<User> targetInchargerUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(inchargerUser.get().getDingdingUserid())).findFirst();
+                pro.setInchargerId(targetInchargerUser.get().getId());
+            }
+            //在新公司创建相同的客户信息
+            /*CustomerInfo customerInfo = customerInfoMapper.selectById(pro.getCustomerId());
+            customerInfo.setId(null);
+            customerInfo.setCompanyId(targetCompanyId);
+            customerInfoMapper.insert(customerInfo);
+            pro.setCustomerId(customerInfo.getId());*/
+            List<ProjectAuditor> projectAuditorList = oldProjectAuditorList.stream().filter(pa -> pa.getProjectId().equals(pro.getId())).collect(Collectors.toList());
+            Optional<User> creatorUser = oldUserList.stream().filter(ou -> ou.getName().equals("李婷婷")).findFirst();
+            Optional<User> targetCreatorUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(creatorUser.get().getDingdingUserid())).findFirst();
+            pro.setCreatorId(targetCreatorUser.get().getId());
+            //当前项目的相关参与人
+            List<String> userList = participationList.stream().filter(pc -> pc.getProjectId().equals(pro.getId())).collect(Collectors.toList()).stream().map(o -> o.getUserId()).collect(Collectors.toList());
+            pro.setId(null);
+            pro.setCompanyId(targetCompanyId);
+            if(list.contains(pro.getProjectCode())){
+                continue;
+            }
+            newProjectList.add(pro);
+            //处理项目审核人
+            for (ProjectAuditor projectAuditor : projectAuditorList) {
+                projectAuditor.setProjectId(pro.getId());
+                projectAuditor.setId(null);
+                Optional<User> first = targetUserList.stream().filter(tu -> tu.getName().equals(projectAuditor.getAuditorName())).findFirst();
+                if(first.isPresent()){
+                    projectAuditor.setAuditorId(first.get().getId());
+                    projectAuditor.setAuditorName(first.get().getName());
+                    newProjectAuditorList.add(projectAuditor);
+                }
+            }
+            for(String s:userList){
+               Participation participation=new Participation();
+               participation.setProjectId(pro.getId());
+                Optional<User> user = oldUserList.stream().filter(ou -> ou.getId().equals(s)).findFirst();
+                Optional<User> targetUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(user.get().getDingdingUserid())).findFirst();
+                participation.setUserId(targetUser.get().getId());
+                newParticipationList.add(participation);
+            }
+        }
+        projectService.saveBatch(newProjectList);
+        projectAuditorService.saveBatch(newProjectAuditorList);
+        participationService.saveBatch(newParticipationList);
+        List<Project> oldProjectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", projectIds));
+        //处理日报
+        List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().in("creator_id", ids));
+        List<Report> changeReportList=new ArrayList<>();
+        for (Report report : reportList) {
+            //项目关联
+            Optional<Project> oldProject = oldProjectList.stream().filter(op -> op.getId().equals(report.getProjectId())).findFirst();
+            Optional<Project> targetProject = projectList.stream().filter(pro -> pro.getProjectCode().equals(oldProject.get().getProjectCode())).findFirst();
+            report.setProjectId(targetProject.get().getId());
+            Optional<User> oldUser = oldUserList.stream().filter(ou -> ou.getId().equals(report.getCreatorId())).findFirst();
+            Optional<User> targetUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(oldUser.get().getDingdingUserid())).findFirst();
+            report.setCreatorId(targetUser.get().getId());
+            //部门数据
+            Optional<Department> oldDepart = oldDepartmentList.stream().filter(od -> od.getDepartmentId().equals(report.getDeptId())).findFirst();
+            Optional<Department> targetDepart = targetDepartmentList.stream().filter(td -> td.getDepartmentName().equals(oldDepart.get().getDepartmentName())).findFirst();
+            report.setDeptId(targetDepart.get().getDepartmentId());
+            //当前审核部门数据
+            Optional<Department> oldAuditDepart = oldDepartmentList.stream().filter(od -> od.getDepartmentId().equals(report.getAuditDeptid())).findFirst();
+            if(oldAuditDepart.isPresent()){
+                Optional<Department> targetAuditDepart =targetDepartmentList.stream().filter(td -> td.getDepartmentName().equals(oldAuditDepart.get().getDepartmentName())).findFirst();
+                report.setAuditDeptid(targetAuditDepart.get().getDepartmentId());
+                report.setAuditDeptManagerid(targetAuditDepart.get().getManagerId());
+            }
+            //是否被驳回
+            if(!StringUtils.isNullOrEmpty(report.getRejectUsername())){
+                Optional<User> rejectUser = targetUserList.stream().filter(tu -> tu.getName().equals(report.getRejectUsername())).findFirst();
+                report.setRejectUserid(rejectUser.get().getId());
+            }
+            report.setCompanyId(targetCompanyId);
+            changeReportList.add(report);
+        }
+        reportService.updateBatchById(changeReportList);
+        return msg;
+    }
 }

+ 209 - 178
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java

@@ -125,9 +125,58 @@ public class DingDingServiceImpl implements DingDingService {
     private boolean isPrivateDeploy;//企业内部应用,私有化部署的情况
     @Value("${dingding.appId}")
     private long appId;//钉钉第三方应用的appId
+
     @Value("${configEnv.isDev}")
     private boolean isDev;//是否是本地开发环境
 
+    @Override
+    public synchronized HttpRespMsg initSystem(String corpid) throws ApiException {
+        System.out.println("========接收到initSystem请求===corpid="+corpid);
+        HttpRespMsg msg = new HttpRespMsg();
+        if (!isPrivateDeploy) {
+            msg.setError("请配置isPrivateDeploy: true");
+        } else {
+            //查找公司对应的记录是否已经添加
+            CompanyDingding dingding = companyDingdingMapper.selectById(corpid);
+            if (dingding == null) {
+                msg.setError("请在company_dingding表中增加corpid:" + corpid);
+            } else if (dingding.getCompanyId() != null) {
+                msg.setError("companyId已存在,如需重新初始化请先重置company_dingding中该条数据的companyId为null");
+            } else {
+
+                Company company = new Company().setCompanyName(dingding.getCorpName())
+                        .setExpirationDate(LocalDateTime.now().plusDays(15));
+                company.setPackageProject(1);
+                company.setPackageExpense(1);
+                company.setPackageCustomer(1);
+                company.setPackageOa(1);
+                companyMapper.insert(company);
+                dingding.setCompanyId(company.getId());
+                companyDingdingMapper.updateById(dingding);
+                //生成工作时长
+                TimeType timeType = new TimeType();
+                timeType.setCompanyId(company.getId());
+                timeTypeMapper.insert(timeType);
+
+                SysRole smanager = sysRoleService.generateDefaultRoles(company.getId());
+
+                //生成项目的成本基线默认条目
+                String[] array = Constant.DEFAULT_BASE_COST_ITEMS;
+                for (String baseItem : array) {
+                    ProjectBasecostSetting setting = new ProjectBasecostSetting();
+                    setting.setName(baseItem);
+                    setting.setCompanyId(company.getId());
+                    projectBasecostSettingMapper.insert(setting);
+                }
+
+                //获取企业内部应用accessToken
+                String innerCorpToken = getInnerCorpToken(dingding);
+                getAuthedDeptsAndUsers(dingding, innerCorpToken);
+            }
+        }
+        return msg;
+    }
+
     @Override
     @Async
     public void corpAuth(String corpid, String corpName, String authUserId, Long agentId) throws ApiException {
@@ -237,40 +286,41 @@ public class DingDingServiceImpl implements DingDingService {
             System.out.println("corpid不存在=="+corpid);
             return "调用失败:corpid不存在==";
         }
-        if (dingding.getExpireTime().isBefore(LocalDateTime.now())) {
-            SysConfig config = sysConfigMapper.selectOne(new QueryWrapper<SysConfig>().eq("param_key", "dingding_suite_ticket"));
-            OapiServiceGetCorpTokenResponse result = getAuthCorpAccessToken(corpid, config.getParamValue());
-            if (result != null) {
-                if (result.getErrcode() == 0L) {
-                    //返回成功
-                    dingding.setAccessToken(result.getAccessToken());
-                    LocalDateTime time = LocalDateTime.now();
-                    time = time.plusSeconds(result.getExpiresIn());//设置token过期时间
-                    dingding.setExpireTime(time);
-                    companyDingdingMapper.updateById(dingding);
+        if (isPrivateDeploy) {
+            //私有化部署,取innerToken
+            accessToken = getInnerCorpToken(dingding);
+        } else {
+            if (dingding.getExpireTime().isBefore(LocalDateTime.now())) {
+                SysConfig config = sysConfigMapper.selectOne(new QueryWrapper<SysConfig>().eq("param_key", "dingding_suite_ticket"));
+                OapiServiceGetCorpTokenResponse result = getAuthCorpAccessToken(corpid, config.getParamValue());
+                if (result != null) {
+                    if (result.getErrcode() == 0L) {
+                        //返回成功
+                        dingding.setAccessToken(result.getAccessToken());
+                        LocalDateTime time = LocalDateTime.now();
+                        time = time.plusSeconds(result.getExpiresIn());//设置token过期时间
+                        dingding.setExpireTime(time);
+                        companyDingdingMapper.updateById(dingding);
 
-                    accessToken = dingding.getAccessToken();
-                } else {
-                    System.out.println(result.getBody());
+                        accessToken = dingding.getAccessToken();
+                    } else {
+                        System.out.println(result.getBody());
+                    }
                 }
+            } else {
+                accessToken = dingding.getAccessToken();
             }
-        } else {
-            accessToken = dingding.getAccessToken();
         }
+
         if (accessToken == null) {
             System.out.println("获取第三方企业accessToke失败");
             return "调用失败";
         } else {
             System.out.println("syncCorpMembs 开始获取部门, accessToken="+accessToken);
-
             //获取授权的部门
             getAuthedDeptsAndUsers(dingding, accessToken);
             return "调用成功";
         }
-
-
-//        String rest = getDepartmentList(dingding.getCompanyId(), corpid, accessToken, 1L, null);
-
     }
 
     private void getAuthedDeptsAndUsers(CompanyDingding dingding, String accessToken) {
@@ -381,12 +431,14 @@ public class DingDingServiceImpl implements DingDingService {
      */
     private String getInnerCorpToken(CompanyDingding dingding) throws ApiException {
         if (dingding.getInnerToken() == null || LocalDateTime.now().isAfter(dingding.getInnerExpireTime())) {
-            DefaultDingTalkClient client= new DefaultDingTalkClient("https://oapi.dingtalk.com/service/get_corp_token");
-            OapiServiceGetCorpTokenRequest req= new OapiServiceGetCorpTokenRequest();
-            req.setAuthCorpid(dingding.getCorpid());
-            OapiServiceGetCorpTokenResponse response= client.execute(req,dingding.getCustomAppkey(),dingding.getCustomAppsecret(),"anything");
+            DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/gettoken");
+            OapiGettokenRequest request = new OapiGettokenRequest();
+            request.setAppkey(dingding.getInnerAppkey());
+            request.setAppsecret(dingding.getInnerAppsecret());
+            request.setHttpMethod("GET");
+            OapiGettokenResponse response = client.execute(request);
             if (!response.isSuccess()) {
-                System.err.println("获取企业内部token:"+response.getErrorCode()+":"+response.getErrmsg());
+                System.err.println("获取企业内部token失败:"+response.getErrorCode()+":"+response.getErrmsg());
             }
             dingding.setInnerToken(response.getAccessToken());
             dingding.setInnerExpireTime(LocalDateTime.now().plusSeconds(response.getExpiresIn()));
@@ -430,10 +482,18 @@ public class DingDingServiceImpl implements DingDingService {
         OapiV2UserGetuserinfoRequest req = new OapiV2UserGetuserinfoRequest();
         req.setCode(code);
         //获取有效的accessToken
-        String accessToken = getValidCorpAccessToken(corpid);
+
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         OapiV2UserGetuserinfoResponse rsp = null;
         try {
+            String accessToken = null;
+            if (isPrivateDeploy) {
+                CompanyDingding companyDingding = companyDingdingMapper.selectById(corpid);
+                accessToken = getInnerCorpToken(companyDingding);
+            } else {
+                accessToken = getValidCorpAccessToken(corpid);
+            }
+
             rsp = client.execute(req, accessToken);
             System.out.println(rsp.getBody());
             JSONObject json = JSONObject.parseObject(rsp.getBody());
@@ -752,6 +812,54 @@ public class DingDingServiceImpl implements DingDingService {
         }
     }
 
+    private void getAddUserDetail(CompanyDingding dingding, String dingdingUserid, String access_token) throws ApiException {
+        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
+        OapiV2UserGetRequest req = new OapiV2UserGetRequest();
+        req.setUserid(dingdingUserid);
+        req.setLanguage("zh_CN");
+        OapiV2UserGetResponse rsp = client.execute(req, access_token);
+//        System.out.println(rsp.getBody());
+        JSONObject resp = JSONObject.parseObject(rsp.getBody());
+        if (resp.getInteger("errcode") == 0) {
+            JSONObject userJson = resp.getJSONObject("result");
+            User user = new User();
+            SysRole defaultRole = sysRoleMapper.selectOne(
+                    new QueryWrapper<SysRole>().eq("company_id", dingding.getCompanyId()).eq("is_default", 1));
+
+            user.setId(SnowFlake.nextId() + "")
+                    .setRoleId(defaultRole.getId())
+                    .setRoleName(defaultRole.getRolename())
+                    .setCompanyId(dingding.getCompanyId())
+                    .setName(userJson.getString("name"))
+                    .setDingdingUserid(dingdingUserid)
+                    .setDingdingUnionid(userJson.getString("unionid"))
+                    .setColor(ColorUtil.randomColor());
+            JSONArray dingdingDeptIds = userJson.getJSONArray("dept_id_list");
+            if (dingdingDeptIds.size() > 0) {
+                //取最后一个部门,一般是最末级部门
+                Integer dpId = dingdingDeptIds.getInteger(dingdingDeptIds.size()-1);
+                DepartmentDingding departmentDingding = departmentDingdingMapper.selectOne(new QueryWrapper<DepartmentDingding>().eq("corpid", dingding.getCorpid())
+                        .eq("dd_deptid", dpId));
+                if (departmentDingding != null) {
+                    Integer departmentId = departmentDingding.getSysDeptid();
+                    if (departmentId != null) {
+                        user.setDepartmentId(departmentDingding.getSysDeptid());
+                        user.setDepartmentCascade(convertDepartmentIdToCascade(departmentId));
+                    }
+                }
+            }
+            //检查用户是否已经存在
+            synchronized (userLock) {
+                List<User> oldList = userMapper.selectList(new QueryWrapper<User>().eq("dingding_userid", dingdingUserid).eq("company_id", dingding.getCompanyId()));
+                if (oldList.size() == 0) {
+                    System.out.println("监听变化,主动新增钉钉用户==" + user.getName());
+                    userMapper.insert(user);
+                }
+            }
+        }
+    }
+
+
     //获取人员详情
     private void getUserDetail(int roleId, String roleName, Integer companyId, Integer departmentId, String dingdingUserid, String access_token) throws ApiException {
         DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/v2/user/get");
@@ -1364,51 +1472,6 @@ public class DingDingServiceImpl implements DingDingService {
         }
     }
 
-//    public HttpRespMsg getLeaveTypeList(String leaveCode, Integer companyId, Integer departmentId, String userId, Integer pageIndex) {
-//        CompanyDingding dingding = companyDingdingMapper.selectOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
-//        HttpRespMsg msg = new HttpRespMsg();
-//        HashMap resultMap = new HashMap();
-//        DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/vacation/type/list");
-//        OapiAttendanceVacationTypeListRequest req = new OapiAttendanceVacationTypeListRequest();
-//        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId).eq("role_name", "超级管理员"));
-////        String oaManagerDid = "2221645448842951";
-//        String oaManagerDid = userList.get(0).getDingdingUserid();
-//        if (leaveCode == null) {
-//            req.setOpUserid(oaManagerDid);
-//            req.setVacationSource("all");
-//            OapiAttendanceVacationTypeListResponse rsp = null;
-//            try {
-//                rsp = client.execute(req, getInnerCorpToken(dingding));
-//                System.out.println(rsp.getBody());
-//                JSONObject json = JSONObject.parseObject(rsp.getBody());
-//                JSONArray result = json.getJSONArray("result");
-//                List<LeaveTypeVO> typeList = new ArrayList<>();
-//                LeaveTypeVO defaultType = null;
-//                for (int i=0;i<result.size(); i++) {
-//                    JSONObject obj = result.getJSONObject(i);
-//                    LeaveTypeVO vo = new LeaveTypeVO();
-//                    vo.leaveCode = obj.getString("leave_code");
-//                    vo.leaveName = obj.getString("leave_name");
-//                    if (vo.leaveName.equals("调休")) {
-//                        defaultType = vo;
-//                    }
-//                    typeList.add(vo);
-//                }
-//                resultMap.put("leaveTypeList", typeList);
-//                if (defaultType == null) defaultType = typeList.get(0);
-//                leaveCode = defaultType.leaveCode;
-//                resultMap.put("defaultLeaveType", defaultType);
-//                System.out.println("本次请求的LeaveCode: "+leaveCode);
-//                reqQuotaList(companyId, dingding, leaveCode, oaManagerDid, departmentId, userId, pageIndex, resultMap);
-//            } catch (ApiException e) {
-//                e.printStackTrace();
-//            }
-//        } else {
-//            reqQuotaList(companyId, dingding, leaveCode, oaManagerDid, departmentId, userId, pageIndex, resultMap);
-//        }
-//        msg.data = resultMap;
-//        return msg;
-//    }
 
     @Override
     public HttpRespMsg syncLeaveQuotaData(Integer companyId) {
@@ -1424,6 +1487,8 @@ public class DingDingServiceImpl implements DingDingService {
         OapiAttendanceVacationTypeListResponse rsp = null;
         try {
             rsp = client.execute(req, getInnerCorpToken(dingding));
+            System.out.println(rsp.getBody());
+            System.out.println(rsp.getErrcode()+":"+rsp.getErrmsg());
             JSONObject json = JSONObject.parseObject(rsp.getBody());
             JSONArray result = json.getJSONArray("result");
             List<LeaveType> typeList = new ArrayList<>();
@@ -1464,6 +1529,78 @@ public class DingDingServiceImpl implements DingDingService {
         return new HttpRespMsg();
     }
 
+    @Override
+    public HttpRespMsg removeCompInfo(String corpid) {
+        HttpRespMsg msg = new HttpRespMsg();
+        CompanyDingding dingding = companyDingdingMapper.selectById(corpid);
+        Integer companyId = dingding.getCompanyId();
+        departmentMapper.delete(new QueryWrapper<Department>().eq("company_id", companyId));
+        departmentDingdingMapper.delete(new QueryWrapper<DepartmentDingding>().eq("corpid", corpid));
+        userMapper.delete(new QueryWrapper<User>().eq("company_id", companyId));
+        sysRoleMapper.delete(new QueryWrapper<SysRole>().eq("company_id", companyId));
+        //sysRoleFunction和sysRoleModule表会级联删除
+        projectBasecostSettingMapper.delete(new QueryWrapper<ProjectBasecostSetting>().eq("company_id", companyId));
+        companyMapper.deleteById(companyId);
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg initSuperManager(String corpid, String name) {
+        HttpRespMsg msg = new HttpRespMsg();
+        CompanyDingding dingding = companyDingdingMapper.selectById(corpid);
+        Integer companyId = dingding.getCompanyId();
+        User user = userMapper.selectOne(new QueryWrapper<User>().eq("name", name).eq("company_id", companyId));
+        if (user == null) {
+            msg.setError("该用户不存在: " + name);
+        } else {
+            SysRole role = sysRoleMapper.selectOne(new QueryWrapper<SysRole>().eq("company_id", companyId).eq("rolename", "超级管理员"));
+            user.setPhone(user.getName());
+            user.setPassword(MD5Util.getPassword("000000"));
+            user.setRoleId(role.getId());
+            user.setRoleName(role.getRolename());
+            userMapper.updateById(user);
+        }
+        return msg;
+    }
+
+    @Override
+    public void userLeaveOrg(String corpId, JSONArray userIdArray, LocalDate leaveDate) {
+        //员工离职,对其标记为离职状态
+        CompanyDingding companyDingding = companyDingdingMapper.selectById(corpId);
+        for (int i=0;i<userIdArray.size(); i++) {
+            String userdingId = userIdArray.getString(i);
+            User user = userMapper.selectOne(new QueryWrapper<User>().eq("company_id", companyDingding.getCompanyId()).eq("dingding_userid", userdingId));
+            if (user != null) {
+                user.setIsActive(0);
+                user.setInactiveDate(leaveDate);
+                userMapper.updateById(user);
+            }
+        }
+    }
+
+    @Override
+    public void userAddOrg(String corpId, JSONArray userIdArray) {
+        //添加了员工,检测是否在我们已经授权的范围内
+        CompanyDingding companyDingding = companyDingdingMapper.selectById(corpId);
+        for (int i=0;i<userIdArray.size(); i++) {
+            String userdingId = userIdArray.getString(i);
+            User user = userMapper.selectOne(new QueryWrapper<User>().eq("company_id", companyDingding.getCompanyId()).eq("dingding_userid", userdingId));
+            if (user == null) {
+                //添加人员
+                try {
+                    String corpAccessToken = getInnerCorpToken(companyDingding);
+                    getAddUserDetail(companyDingding, userdingId, corpAccessToken);
+                } catch (ApiException e) {
+                    e.printStackTrace();
+                }
+            } else {
+                //已存在,更新状态
+                user.setIsActive(1);
+                userMapper.updateById(user);
+            }
+        }
+    }
+
 
     //同步公司全部人员的假期余额
     private void syncQuotaList(String leaveCode,String oaManagerDid, CompanyDingding dingding, List<User> userList, long offset) {
@@ -1537,112 +1674,6 @@ public class DingDingServiceImpl implements DingDingService {
             }
         }
     }
-//
-//    private void reqQuotaList(Integer companyId, CompanyDingding dingding,
-//                                             String leaveCode, String oaManagerDid, Integer departmentId, String userId, Integer pageIndex, HashMap resultMap) {
-//        //再调用查看假期余额的接口
-//        long pageSize = 50L;
-//        DefaultDingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/topapi/attendance/vacation/quota/list");
-//        OapiAttendanceVacationQuotaListRequest quoataReq = new OapiAttendanceVacationQuotaListRequest();
-//        quoataReq.setLeaveCode(leaveCode);
-//        quoataReq.setOpUserid(oaManagerDid);
-//        List<User> userList = null;
-//        String userIds = null;
-//        if (StringUtils.isEmpty(userId)) {
-//            int total = 0;
-//            if (departmentId == null) {
-//                //全部员工,包括离职的
-//                QueryWrapper<User> queryWrapper = new QueryWrapper<User>();
-//                queryWrapper.select("distinct dingding_userid, name").eq("company_id", companyId);
-//                userList = userMapper.selectList(queryWrapper);
-//                total = userList.size();
-//            } else {
-//                //指定部门
-//                Department dept = departmentMapper.selectById(departmentId);
-//                List<Department> allDept = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
-//                List<Department> allSubDepts = getSubDeptList(allDept, dept);
-//                userList = userMapper.selectList(
-//                        new QueryWrapper<User>().select("distinct dingding_userid, name")
-//                                .in("department_id", allSubDepts.stream().map(Department::getDepartmentId).collect(Collectors.toList())));
-//                total = userList.size();
-//            }
-//            String collect = userList.stream().map(User::getDingdingUserid).collect(Collectors.joining(","));
-//            userIds = collect;
-//            resultMap.put("total", total);
-//        } else {
-//            userList = new ArrayList<>();
-//            userList = userMapper.selectList(new QueryWrapper<User>().in("id", ListUtil.convertLongIdsArrayToList(userId)));
-//            userIds = userList.stream().map(User::getDingdingUserid).collect(Collectors.joining(","));
-//            resultMap.put("total", userList.size());
-//        }
-//
-//        System.out.println("请求的size="+userList.size());
-//        System.out.println("长度=="+userIds.length());
-//        List<LeaveQuotaNum> quotaNumList = new ArrayList<>();
-//        for (User user : userList) {
-//            LeaveQuotaNum quotaNum = new LeaveQuotaNum();
-//            quotaNum.userDingdingId = user.getDingdingUserid();
-//            quotaNum.name = user.getName();
-//            quotaNum.leaveCode = leaveCode;
-//            quotaNumList.add(quotaNum);
-//        }
-//
-//        if (quotaNumList.size() == 0) {
-//            //无员工数据了
-//            return;
-//        }
-//        quoataReq.setUserids(userIds);
-//        quoataReq.setOffset(0L);
-//        quoataReq.setSize(pageSize);
-//        OapiAttendanceVacationQuotaListResponse quotaListResponse = null;
-//        try {
-//            quotaListResponse = client.execute(quoataReq, getInnerCorpToken(dingding));
-//        } catch (ApiException e) {
-//            e.printStackTrace();
-//        }
-//        System.out.println(quotaListResponse.getBody()+":"+quotaListResponse.getErrmsg());
-//        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-//        JSONObject json = JSONObject.parseObject(quotaListResponse.getBody());
-//        DecimalFormat df = new DecimalFormat("#0.0");
-//        if (json.getInteger("errcode") == 0) {
-//            JSONObject result = json.getJSONObject("result");
-//            JSONArray leaveQuotas = result.getJSONArray("leave_quotas");
-//            if (leaveQuotas != null) {
-//                for (int i=0;i<leaveQuotas.size(); i++) {
-//                    JSONObject jsonObject = leaveQuotas.getJSONObject(i);
-//                    String userDDid = jsonObject.getString("userid");
-//                    for (LeaveQuotaNum quotaNum : quotaNumList) {
-//                        if (quotaNum.userDingdingId.equals(userDDid)) {
-//                            Date startDate = new Date(jsonObject.getLongValue("start_time"));
-//                            quotaNum.startTime = sdf.format(startDate);
-//                            Date endDate = new Date(jsonObject.getLongValue("end_time"));
-//                            quotaNum.endTime = sdf.format(endDate);
-//                            Integer quota_num_per_hour = jsonObject.getInteger("quota_num_per_hour");
-//                            if (quota_num_per_hour != null) {
-//                                //按小时为单位的情况
-//                                quotaNum.quotaInHours = df.format(quota_num_per_hour*1.0/100);
-//                                Integer used_num_per_hour = jsonObject.getInteger("used_num_per_hour");
-//                                quotaNum.usedInHours = df.format(used_num_per_hour*1.0/100);
-//                                //计算剩余
-//                                quotaNum.leftInHours = df.format((quota_num_per_hour - used_num_per_hour)*1.0/100);
-//                            } else {
-//                                //按天单位的情况
-//                                Integer quota_num_per_day = jsonObject.getInteger("quota_num_per_day");
-//                                quotaNum.quotaInDays = df.format(quota_num_per_day*1.0/100);
-//                                Integer used_num_per_day = jsonObject.getInteger("used_num_per_day");
-//                                quotaNum.usedInDays = df.format(used_num_per_day*1.0/100);
-//                                //计算剩余
-//                                quotaNum.leftInDays = df.format((quota_num_per_day - used_num_per_day)*1.0/100);
-//                            }
-//                            System.out.println(quotaNum.name+" 假期额度="+quotaNum.quotaInDays);
-//                            break;
-//                        }
-//                    }
-//                }
-//                resultMap.put("records", quotaNumList);
-//            }
-//        }
-//    }
 
     public void activateSuite(String authCorpid, String tmpAuthCode) throws ApiException {
         DingTalkClient client= new DefaultDingTalkClient("https://oapi.dingtalk.com/service/activate_suite?suite_access_token=" + getDDSuiteAccessToken());

+ 260 - 15
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ExpenseSheetServiceImpl.java

@@ -5,30 +5,36 @@ import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.management.platform.entity.ExpenseItem;
-import com.management.platform.entity.ExpenseSheet;
-import com.management.platform.entity.Project;
-import com.management.platform.entity.User;
-import com.management.platform.mapper.ExpenseItemMapper;
-import com.management.platform.mapper.ExpenseSheetMapper;
-import com.management.platform.mapper.UserMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.entity.*;
+import com.management.platform.entity.vo.SysRichFunction;
+import com.management.platform.mapper.*;
 import com.management.platform.service.ExpenseItemService;
 import com.management.platform.service.ExpenseSheetService;
-import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
+import org.apache.poi.EncryptedDocumentException;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.ss.usermodel.CellType;
+import org.apache.poi.xssf.usermodel.XSSFCell;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFSheet;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
-import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
+import java.io.*;
+import java.math.BigDecimal;
+import java.time.Instant;
 import java.time.LocalDate;
+import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -49,8 +55,12 @@ public class ExpenseSheetServiceImpl extends ServiceImpl<ExpenseSheetMapper, Exp
     private ExpenseItemService expenseItemService;
     @Resource
     private ExpenseItemMapper expenseItemMapper;
-
-
+    @Resource
+    private ProjectMapper projectMapper;
+    @Resource
+    private SysFunctionMapper sysFunctionMapper;
+    @Resource
+    private ExpenseTypeMapper expenseTypeMapper;
     @Override
     public HttpRespMsg add(ExpenseSheet sheet, String items, String userId) {
         HttpRespMsg msg = new HttpRespMsg();
@@ -184,4 +194,239 @@ public class ExpenseSheetServiceImpl extends ServiceImpl<ExpenseSheetMapper, Exp
         expenseSheetMapper.updateById(sheet);
         return new HttpRespMsg();
     }
+
+    @Override
+    public HttpRespMsg importData(HttpServletRequest request,MultipartFile[] files) {
+        HttpRespMsg msg = new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        //然后处理文件
+        for (MultipartFile multipartFile : files) {
+            String fileName = multipartFile.getOriginalFilename();
+            File file = new File(fileName == null ? "file" : fileName);
+            InputStream inputStream = null;
+            OutputStream outputStream = null;
+            try {
+                inputStream = multipartFile.getInputStream();
+                outputStream = new FileOutputStream(file);
+                byte[] buffer = new byte[4096];
+                int temp = 0;
+                while ((temp = inputStream.read(buffer, 0, 4096)) != -1) {
+                    outputStream.write(buffer, 0, temp);
+                }
+                inputStream.close();
+                outputStream.close();
+                //然后解析表格
+                XSSFWorkbook workbook = new XSSFWorkbook(file);
+                //我们只需要第一个sheet
+                XSSFSheet sheet = workbook.getSheetAt(0);
+                //获取全部人员
+                List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", user.getCompanyId()));
+                List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", user.getCompanyId()));
+                List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "费用审核");
+                List<ExpenseType> expenseTypeList = expenseTypeMapper.selectList(null);
+                //由于第一行需要指明报销人列对应的标题
+                XSSFRow firstRow = sheet.getRow(2);
+                if (firstRow == null) {
+                    msg.setError("错误数据,导入失败");
+                    return msg;
+                }
+                //报销人 填报日期 费用类型 备注
+                XSSFCell reimburserCell = firstRow.getCell(0);
+                XSSFCell reportDateCell = firstRow.getCell(1);
+                XSSFCell expenseTypeCell = firstRow.getCell(2);
+                XSSFCell remarksCell = firstRow.getCell(3);
+
+                if (reimburserCell != null) reimburserCell.setCellType(CellType.STRING);
+                if (reportDateCell != null) reportDateCell.setCellType(CellType.NUMERIC);
+                if (expenseTypeCell != null) expenseTypeCell.setCellType(CellType.STRING);
+                if (remarksCell != null) remarksCell.setCellType(CellType.STRING);
+                if (reimburserCell == null) {//报销人为空的直接跳过
+                    throw new Exception("报销人名称不能为空");
+                }
+                if (expenseTypeCell == null) {
+                    throw new Exception("费用类型不能为空");
+                }
+                if (reportDateCell == null) {
+                    throw new Exception("填报日期不能为空");
+                }
+                ExpenseSheet expenseSheet = new ExpenseSheet();
+                if(functionList.size()>0){
+                    expenseSheet.setStatus(0);
+                }else {
+                    expenseSheet.setStatus(1);
+                }
+                //处理编号
+                DateTimeFormatter yyyyMMdd = DateTimeFormatter.ofPattern("yyyyMMdd");
+                String format = yyyyMMdd.format(LocalDate.now());
+                List<ExpenseSheet> list = expenseSheetMapper.selectList(new QueryWrapper<ExpenseSheet>()
+                        .eq("company_id", user.getCompanyId())
+                        .like("code", format).orderByDesc("id").last("limit 1"));
+                int start = 1;
+                if (list.size() > 0) {
+                    String code = list.get(0).getCode();
+                    code = code.substring(format.length(), code.length());
+                    System.out.println("code=====" + code);
+                    if (code.length() > 0) {
+                        start = Integer.parseInt(code) + 1;
+                    }
+                }
+                expenseSheet.setCode(format + start);
+                expenseSheet.setCompanyId(user.getCompanyId());
+                LocalDate localDate = Instant.ofEpochMilli(reportDateCell.getDateCellValue().getTime())
+                        .atZone(ZoneId.systemDefault()).toLocalDate();
+                expenseSheet.setCreateDate(localDate);
+                Optional<User> first = userList.stream().filter(us -> us.getName().equals(reimburserCell.getStringCellValue())).findFirst();
+                if (first.isPresent()) {
+                    expenseSheet.setOwnerId(first.get().getId());
+                    expenseSheet.setOwnerName(first.get().getName());
+                } else {
+                    throw new Exception("报销人[" +reimburserCell+"]不存在");
+                }
+                expenseSheet.setOperatorId(user.getId());
+                expenseSheet.setRemark(remarksCell.getStringCellValue());
+                switch (expenseTypeCell.getStringCellValue()) {
+                    case "一般":
+                        expenseSheet.setType(0);
+                        break;
+                    case "差旅":
+                        expenseSheet.setType(1);
+                        break;
+                    case "外包":
+                        expenseSheet.setType(2);
+                        break;
+                }
+                expenseSheetMapper.insert(expenseSheet);
+                //前提需要指定第一二行为报销人数据 从第三行数据开始处理 报销单据详情
+                int rowNum = sheet.getLastRowNum();
+                //计算总费用
+                BigDecimal bigDecimal = new BigDecimal(0);
+                List<ExpenseItem> expenseItemList = new ArrayList<>();
+                int i=0;
+                for (int rowIndex = 5; rowIndex <= rowNum; rowIndex++) {
+                    XSSFRow row = sheet.getRow(rowIndex);
+                    if (row == null) {
+                        continue;
+                    }
+                    //跳过空行
+                    if (ExcelUtil.isRowEmpty(row)) {
+                        continue;
+                    }
+                    //项目编号 费用日期 发拍种类 费用类型 费用金额 发票号 税率 税额 备注
+                    XSSFCell codeCell = row.getCell(0);
+                    XSSFCell happenDateCell = row.getCell(1);
+                    XSSFCell invoiceTypeCell = row.getCell(2);
+                    XSSFCell subExpenseTypeCell = row.getCell(3);
+                    XSSFCell amountCell = row.getCell(4);
+                    XSSFCell invoiceNoCell = row.getCell(5);
+                    XSSFCell taxPercentCell = row.getCell(6);
+                    XSSFCell remarkCell = row.getCell(7);
+
+                    if (codeCell != null) codeCell.setCellType(CellType.STRING);
+                    if (happenDateCell != null) happenDateCell.setCellType(CellType.NUMERIC);
+                    if (invoiceTypeCell != null) invoiceTypeCell.setCellType(CellType.STRING);
+                    if (subExpenseTypeCell != null) subExpenseTypeCell.setCellType(CellType.STRING);
+                    if (amountCell != null) amountCell.setCellType(CellType.NUMERIC);
+                    if (invoiceNoCell != null) invoiceNoCell.setCellType(CellType.STRING);
+                    if (taxPercentCell != null) taxPercentCell.setCellType(CellType.NUMERIC);
+                    if (remarkCell != null) remarkCell.setCellType(CellType.STRING);
+                    //项目为空 直接跳过
+                    if(codeCell==null){
+                        continue;
+                    }
+                    if(happenDateCell.toString().trim().equals("")||happenDateCell==null){
+                        msg.setError("费用日期不能为空");
+                        return msg;
+                    }
+                    ExpenseItem expenseItem = new ExpenseItem();
+                    System.out.println(codeCell.getStringCellValue());
+                    Optional<Project> project = projectList.stream().filter(pro ->(StringUtils.isEmpty(pro.getProjectCode())?"":pro.getProjectCode()).equals(codeCell.getStringCellValue())
+                            || (StringUtils.isEmpty(pro.getProjectName())?"":pro.getProjectName()).equals(codeCell.getStringCellValue())).findFirst();
+                    if (project.isPresent()) {
+                        expenseItem.setProjectId(project.get().getId());
+                    } else {
+                        throw new Exception("项目编号/项目名称为[" + codeCell.getStringCellValue() + "]的项目不存在");
+                    }
+                    expenseItem.setExpenseId(expenseSheet.getId());
+                    LocalDate happenDate = Instant.ofEpochMilli(happenDateCell.getDateCellValue().getTime())
+                            .atZone(ZoneId.systemDefault()).toLocalDate();
+                    expenseItem.setHappenDate(String.valueOf(happenDate));
+                    if(invoiceTypeCell!=null){
+                        switch (invoiceTypeCell.getStringCellValue()) {
+                            case "增值税专用发票":
+                                expenseItem.setInvoiceType(0);
+                                break;
+                            case "增值税普通发票":
+                                expenseItem.setInvoiceType(1);
+                                break;
+                        }
+                    }
+                    if(subExpenseTypeCell!=null){
+                        List<String> stringList = expenseTypeList.stream().filter(et -> et.getMainType().equals(expenseSheet.getType())).map(et -> et.getTypeName()).collect(Collectors.toList());
+                        if(!stringList.contains(subExpenseTypeCell.getStringCellValue())){
+                            msg.setError("当前费用类型["+expenseTypeCell.getStringCellValue()+"]中不存在["+subExpenseTypeCell.getStringCellValue()+"]类型");
+                            return msg;
+                        }
+                        expenseItem.setExpenseType(subExpenseTypeCell.getStringCellValue());
+                    }
+                    if(invoiceNoCell!=null){
+                        expenseItem.setInvoiceNo(invoiceNoCell.getStringCellValue());
+                    }
+                    if(amountCell!=null){
+                        expenseItem.setAmount(amountCell.getNumericCellValue());
+                        //计算税额
+                        BigDecimal bd = new BigDecimal(amountCell.getNumericCellValue());
+                        //原始金额
+                        BigDecimal divide = bd.divide(BigDecimal.valueOf((1 + taxPercentCell.getNumericCellValue())),2,BigDecimal.ROUND_HALF_UP);
+                        BigDecimal subtract = bd.subtract(divide);
+                        bigDecimal=bigDecimal.add(BigDecimal.valueOf(amountCell.getNumericCellValue()));
+                        expenseItem.setTaxValue(subtract.doubleValue());
+                    }
+                    if(remarkCell!=null){
+                        expenseItem.setRemark(remarkCell.getStringCellValue());
+                    }
+                    if(taxPercentCell!=null){
+                        expenseItem.setTaxPercent(taxPercentCell.getNumericCellValue());
+                    }
+
+                    expenseItemList.add(expenseItem);
+                    i++;
+                }
+                expenseSheet.setTicketNum(i);
+                expenseSheet.setTotalAmount(bigDecimal.doubleValue());
+                expenseSheetMapper.updateById(expenseSheet);
+                expenseItemService.saveBatch(expenseItemList);
+            } catch (IOException e) {
+                e.printStackTrace();
+            } catch (NullPointerException e) {
+                e.printStackTrace();
+                msg.setError("数据格式有误或存在空数据 导入失败");
+                return msg;
+            } catch (InvalidFormatException e) {
+                e.printStackTrace();
+                msg.setError("文件格式错误,如果安装了加密软件需要先解密再上传");
+            } catch (EncryptedDocumentException e) {
+                e.printStackTrace();
+                msg.setError("文件加密状态,需要先解除加密状态再上传");
+                return msg;
+            } catch (Exception e) {
+                e.printStackTrace();
+                msg.setError("上传失败:" + e.getMessage());
+                return msg;
+            } finally {
+                //关闭流
+                try {
+                    if (outputStream != null && inputStream != null) {
+                        outputStream.close();
+                        inputStream.close();
+                        System.out.println("流已关闭");
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+//            file.deleteOnExit();//程序退出时删除临时文件
+                System.out.println(file.delete());
+            }
+        }
+        return msg;
+    }
 }

+ 114 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -33,10 +33,12 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.math.BigDecimal;
-import java.text.DecimalFormat;
-import java.text.NumberFormat;
-import java.text.SimpleDateFormat;
+import java.sql.Timestamp;
+import java.text.*;
+import java.time.Duration;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -3081,9 +3083,117 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
         return msg;
     }
 
+    @Override
+    public HttpRespMsg getUserReportTimelinessRate(HttpServletRequest request, String startDate, String endDate, Integer departmentId, String userId,Integer pageIndex,Integer pageSize) {
+        HttpRespMsg msg=new HttpRespMsg();
+        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        DecimalFormat dft =  new DecimalFormat("0%");
+        LocalDateTime sDate = LocalDate.parse(startDate).atTime(LocalTime.now());
+        LocalDateTime eDate = LocalDate.parse(endDate).atTime(LocalTime.now());
+        Duration duration=Duration.between(sDate,eDate);
+        long days = duration.toDays();
+        Integer companyId= userMapper.selectById(request.getHeader("token")).getCompanyId();
+        TimeType timeType = timeTypeMapper.selectById(companyId);
+        String alertTime = timeType.getAlertTime();
+        Integer alertType = timeType.getAlertType();
+        LocalTime alert = LocalTime.parse(alertTime);
+        List<Map<String,Object>> reportList=reportMapper.getUserReportTimelinessRate(companyId,startDate,endDate);
+        System.out.println(reportList);
+        QueryWrapper<User> queryWrapper=new QueryWrapper();
+        queryWrapper.eq("company_id",companyId);
+        if(departmentId!=null){
+            queryWrapper.eq("department_id",departmentId);
+        }
+        if(userId!=null){
+            queryWrapper.eq("id",userId);
+        }
+        queryWrapper.eq("is_active",1).orderByAsc("department_id");
+        List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
+        List<User> userList;
+        long total=0;
+        if(pageIndex!=null&&pageSize!=null){
+            IPage<User>  userIPage = userMapper.selectPage(new Page<>(pageIndex,pageSize),queryWrapper);
+            userList=userIPage.getRecords();
+            total=userIPage.getTotal();
+        }else {
+            userList = userMapper.selectList(queryWrapper);
+        }
+        Map<Object, List<Map<String, Object>>> listMap = reportList.stream().collect(Collectors.groupingBy(rp -> rp.get("userName")));
+        List<TimelinessRateVO> resultList=new ArrayList<>();
+        for (User user : userList){
+            TimelinessRateVO timelinessRateVO=new TimelinessRateVO();
+            timelinessRateVO.setUserName(user.getName());
+            timelinessRateVO.setTimelinessRate(String.valueOf(dft.format(0)));
+            Optional<Department> first = departmentList.stream().filter(dp -> dp.getDepartmentId().equals(user.getDepartmentId())).findFirst();
+            if(first.isPresent()){
+                timelinessRateVO.setDepartmentName(first.get().getDepartmentName());
+            }
+            List<Map<String, Object>> mapList = listMap.get(user.getName());
+            if(mapList!=null){
+                int num=0;
+                for (Map<String, Object> map : mapList) {
+                    Object date = map.get("createDate");
+                    //去掉sql返回的毫秒值
+                    Timestamp timestamp = (Timestamp) map.get("createTime");
+                    LocalDate createDate = LocalDate.parse(String.valueOf(date));
+                    LocalDate createTimeDate =timestamp.toLocalDateTime().toLocalDate();
+                    LocalTime createTime =timestamp.toLocalDateTime().toLocalTime();
+                    //根据提醒类型 判断比较昨天还是今天
+                    if(alertType==1){
+                         createDate=createDate.plusDays(1);
+                    }
+                    if(createTimeDate.isBefore(createDate)||(createTimeDate.isEqual(createDate)&&createTime.isBefore(alert))){
+                        num++;
+                    }
+                }
+                BigDecimal bigDecimal=new BigDecimal(num);
+                BigDecimal divide;
+                if(days!=0){
+                     divide = bigDecimal.divide(BigDecimal.valueOf(days), 2, BigDecimal.ROUND_HALF_UP);
+                }else if(days==0&&num!=0){
+                    divide=new BigDecimal(1);
+                }else{
+                    divide=new BigDecimal(0);
+                }
+                String number = dft.format(divide);
+                timelinessRateVO.setTimelinessRate(String.valueOf(number));
+            }
+            resultList.add(timelinessRateVO);
+        }
+        HashMap map=new HashMap();
+        map.put("total",total);
+        map.put("list",resultList);
+        msg.data=map;
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg exportReportTimelinessRate(HttpServletRequest request, String startDate, String endDate) {
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        HttpRespMsg msg = getUserReportTimelinessRate(request, startDate, endDate, null, null, null, null);
+        HashMap map= (HashMap) msg.data;
+        List<TimelinessRateVO> list = (List<TimelinessRateVO>) map.get("list");
+        String[] s={"人员","部门","填报及时率"};
+        List<String> titleString = Arrays.asList(s);
+        List<List<String>> dataList=new ArrayList<>();
+        dataList.add(titleString);
+        for (TimelinessRateVO timelinessRateVO : list) {
+            List<String> item=new ArrayList<>();
+            item.add(timelinessRateVO.getUserName());
+            item.add(timelinessRateVO.getDepartmentName());
+            item.add(timelinessRateVO.getTimelinessRate());
+            dataList.add(item);
+        }
+        //生成excel文件导出
+        String fileName = "人员填报及时率统计_"+System.currentTimeMillis();
+        String resp = ExcelUtil.exportGeneralExcelByTitleAndList(fileName , dataList, path);
+        httpRespMsg.data = resp;
+        return httpRespMsg;
+    }
+
 
     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> 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) {

+ 30 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportExtraDegreeServiceImpl.java

@@ -3,7 +3,10 @@ package com.management.platform.service.impl;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.entity.ReportExtraDegree;
+import com.management.platform.entity.TimeType;
+import com.management.platform.entity.User;
 import com.management.platform.mapper.ReportExtraDegreeMapper;
+import com.management.platform.mapper.TimeTypeMapper;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.service.ReportExtraDegreeService;
 import com.management.platform.util.ExcelUtil;
@@ -14,6 +17,7 @@ import org.apache.poi.ss.usermodel.Row;
 import org.apache.poi.ss.usermodel.Sheet;
 import org.apache.poi.ss.usermodel.Workbook;
 import org.apache.poi.ss.usermodel.WorkbookFactory;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -36,10 +40,14 @@ import java.util.stream.Collectors;
  */
 @Service
 public class ReportExtraDegreeServiceImpl extends ServiceImpl<ReportExtraDegreeMapper, ReportExtraDegree> implements ReportExtraDegreeService {
+    @Value(value = "${upload.path}")
+    String path;
     @Resource
     ReportExtraDegreeMapper reportExtraDegreeMapper;
     @Resource
     UserMapper userMapper;
+    @Resource
+    TimeTypeMapper timeTypeMapper;
     @Override
     public HttpRespMsg importData(HttpServletRequest request, MultipartFile multipartFile) {
         HttpRespMsg msg=new HttpRespMsg();
@@ -132,4 +140,26 @@ public class ReportExtraDegreeServiceImpl extends ServiceImpl<ReportExtraDegreeM
             System.out.println(file.delete());
         }
     }
+
+    @Override
+    public HttpRespMsg exportData(HttpServletRequest request) {
+        HttpRespMsg httpRespMsg=new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        List<ReportExtraDegree> reportExtraDegreeList = reportExtraDegreeMapper.selectList(new QueryWrapper<ReportExtraDegree>().eq("company_id", user.getCompanyId()));
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
+        List<String> titleList=new ArrayList<>();
+        titleList.add(timeType.getCustomDegreeName()+"名称");
+        List<List<String>> dataList=new ArrayList<>();
+        dataList.add(titleList);
+        for (ReportExtraDegree reportExtraDegree : reportExtraDegreeList) {
+            List<String> itemList=new ArrayList<>();
+            itemList.add(reportExtraDegree.getName());
+            dataList.add(itemList);
+        }
+        //生成excel文件导出
+        String fileName = timeType.getCustomDegreeName()+"_"+System.currentTimeMillis();
+        String resp = ExcelUtil.exportGeneralExcelByTitleAndList(fileName , dataList, path);
+        httpRespMsg.data = resp;
+        return httpRespMsg;
+    }
 }

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

@@ -859,7 +859,11 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             if (functionList.size() == 0) {//没有全员审核的权限
                 leaderId = curUser.getId();
             }
-            List<Map<String, Object>> auditReportList = reportMapper.getAuditReportList(date, companyId, departmentId, projectId, leaderId, isEngeering, startDate, endDate, userId);
+            List<String> targetUids = null;
+            if (!StringUtils.isEmpty(userId)) {
+                targetUids = ListUtil.convertLongIdsArrayToList(userId);
+            }
+            List<Map<String, Object>> auditReportList = reportMapper.getAuditReportList(date, companyId, departmentId, projectId, leaderId, isEngeering, startDate, endDate, targetUids);
             //抽取姓名,进行分组
             List<Map<String, Object>> nameList = new ArrayList<Map<String, Object>>();
             Map<String, Object> lastName = null;

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

@@ -129,7 +129,10 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
         queryWrapper.orderByAsc("orderitem");
 
         List<SysModule> modules = sysModuleMapper.selectList(queryWrapper);
-
+        if (modules.size() == 0) {
+            System.err.println("请先初始化sys_module和sys_function表");
+            return null;
+        }
         QueryWrapper<SysFunction> functionQueryWrapper = new QueryWrapper<SysFunction>().eq("package_time", 1);
         if (company.getPackageProject() == 1) {
             functionQueryWrapper.or().eq("package_project", 1);

+ 7 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java

@@ -1258,8 +1258,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
             List<Department> deptList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", user.getCompanyId()));
             for (User item : data) {
                 if (item.getDepartmentId() != 0) {
-                    Department department = deptList.stream().filter(d->d.getDepartmentId().equals(item.getDepartmentId())).findFirst().get();
-                    item.setDepartmentName(department.getDepartmentName());
+                    Optional<Department> first = deptList.stream().filter(d -> d.getDepartmentId().equals(item.getDepartmentId())).findFirst();
+                    if(first.isPresent()){
+                        Department department =first.get();
+                        item.setDepartmentName(department.getDepartmentName());
+                    }
                 }
                 HSSFRow row = sheet.createRow(rowNum);
                 row.createCell(0).setCellValue(rowNum);
@@ -1282,9 +1285,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
             //返回生成的文件地址/upload文件夹下
             httpRespMsg.data = "/upload/" + fileUrlSuffix;
         } catch (NullPointerException e) {
+            e.printStackTrace();
             httpRespMsg.setError("验证失败或缺少数据");
             return httpRespMsg;
         } catch (IOException e) {
+            e.printStackTrace();
             httpRespMsg.setError("文件生成错误");
             return httpRespMsg;
         }

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

@@ -145,7 +145,7 @@ public class TimingTask {
         List<Integer> compIds = timeTypeList.stream().map(TimeType::getCompanyId).collect(Collectors.toList());
         //企业内部应用才有权限调用
         List<CompanyDingding> dingdingList = companyDingdingService.list(new QueryWrapper<CompanyDingding>().in("company_id", compIds)
-                .isNotNull("custom_appkey"));
+                .isNotNull("inner_appkey"));
         System.out.println("==========获取钉钉内部应用的数量是:"+dingdingList.size());
         if (dingdingList.size() > 0) {
             DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@@ -268,7 +268,7 @@ public class TimingTask {
         List<Integer> companyIds = timeTypes.stream().map(TimeType::getCompanyId).collect(Collectors.toList());
         List<CompanyDingding> dingdingList = companyDingdingService.list(
                 new QueryWrapper<CompanyDingding>().in("company_id", companyIds)
-                    .isNotNull("custom_appkey"));
+                    .isNotNull("inner_appkey"));
         //每分钟5000条,待后续处理
         for (CompanyDingding dingding : dingdingList) {
             List<BusinessTrip> businessTrips = businessTripMapper.selectList(new QueryWrapper<BusinessTrip>().eq("company_id", dingding.getCompanyId())

+ 1 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java

@@ -9,6 +9,7 @@ import java.io.File;
 import java.io.FileOutputStream;
 import java.io.OutputStream;
 import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 
 public class ExcelUtil {

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/application-dev.yml

@@ -13,7 +13,7 @@ spring:
       max-request-size: 100MB
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://127.0.0.1:3306/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+    url: jdbc:mysql://127.0.0.1:3306/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&&useSSL=false
     username: root
     password: HuoshiDB@2022
     hikari:

+ 6 - 8
fhKeeper/formulahousekeeper/management-platform/src/main/resources/application-mld.yml

@@ -1,4 +1,3 @@
-# 重庆美莱德服务器配置
 server:
   port: 10010
   tomcat:
@@ -14,9 +13,9 @@ spring:
       max-request-size: 100MB
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://10.1.10.33:3306/man_mld?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+    url: jdbc:mysql://127.0.0.1:3306/man_private?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&&useSSL=false
     username: root
-    password: Mld2022@#
+    password: HuoshiDB@2022
     hikari:
       maximum-pool-size: 10
       minimum-idle: 3
@@ -26,7 +25,7 @@ spring:
     # redis
     redis:
       host: 127.0.0.1
-      port: 6379
+      port: 6479
       timeout: 3
       # password:
       pool:
@@ -84,9 +83,6 @@ upload:
   path: /www/staticproject/timesheet/upload/
 
 
-
-
-
 ##actuator健康检查配置
 management:
   security:
@@ -103,4 +99,6 @@ management:
       enabled: false
 
 configEnv:
-  isDev: false
+  isDev: true
+  # 是否是私有化部署,企业内部应用
+  isPrivateDeploy: true

+ 4 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml

@@ -14,7 +14,7 @@ spring:
       location: C:/upload/
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://47.101.180.183:3306/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+    url: jdbc:mysql://47.101.180.183:3306/man_dev?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&useSSL=false
     username: root
     password: HuoshiDB@2022
     hikari:
@@ -122,6 +122,9 @@ referer:
     - mobworktime.ttkuaiban.com
     - worktime.ttkuaiban.com
     - app71020.eapps.dingtalkcloud.com
+    - mldmobworktime.ttkuaiban.com
+    - mldworktime.ttkuaiban.com
+    - 47.101.180.183
 excludeUrls: /wxcorp/*,/wxcorp/*/*,/dingding/*,/error,/testClient,/corpWXAuth,/wx-corp-info/*,/clean/*,/innerRoles/*
 
 #企业微信相关参数

+ 4 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/CompanyDingdingMapper.xml

@@ -12,15 +12,16 @@
         <result column="access_token" property="accessToken" />
         <result column="expire_time" property="expireTime" />
         <result column="agent_id" property="agentId" />
-        <result column="custom_appkey" property="customAppkey" />
-        <result column="custom_appsecret" property="customAppsecret" />
+        <result column="inner_appkey" property="innerAppkey" />
+        <result column="inner_appsecret" property="innerAppsecret" />
         <result column="inner_token" property="innerToken" />
         <result column="inner_expire_time" property="innerExpireTime" />
+        <result column="web_url" property="webUrl" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        corpid, corp_name, auth_user_id, indate, company_id, access_token, expire_time, agent_id, custom_appkey, custom_appsecret, inner_token, inner_expire_time
+        corpid, corp_name, auth_user_id, indate, company_id, access_token, expire_time, agent_id, inner_appkey, inner_appsecret, inner_token, inner_expire_time, web_url
     </sql>
 
 </mapper>

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

@@ -288,8 +288,11 @@
         <if test="date != null and date != ''">
             AND a.create_date=#{date}
         </if>
-        <if test="targetUserId != null and targetUserId != ''">
-            AND a.creator_id=#{targetUserId}
+        <if test="targetUserId != null">
+            AND a.creator_id in
+            <foreach collection="targetUserId" item="tid" separator="," open="(" close=")" index="index">
+            #{tid}
+            </foreach>
         </if>
         <if test="startDate != null and startDate != '' and endDate != null and endDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -621,4 +624,13 @@
     <select id="getOneProjectBaseCost" resultType="java.util.HashMap">
         SELECT basecost_id as basecostId, IFNULL(SUM(cost), 0) AS cost FROM report WHERE state = 1 and basecost_id > 0 AND project_id = #{projectId} group by basecost_id
     </select>
+
+    <select id="getUserReportTimelinessRate" resultType="java.util.Map">
+        select `user`.name as userName,report.create_date as createDate,report.create_time as createTime from report
+        left join `user` on `user`.id=report.creator_id
+        where report.company_id=#{companyId}
+        and report.state=1
+        and report.create_date between #{startDate} and #{endDate}
+        group by `user`.id,report.create_date
+    </select>
 </mapper>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/人员导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/员工工时导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/员工工时统计模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/员工项目分摊比例导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/工时管家使用说明_基础版.docx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/工时管家使用说明_建筑工程专业版.docx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/工时管家使用说明_项目管理专业版.docx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/新增客户导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/研究中心导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/资源需求导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/项目任务导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/src/main/resources/upload/项目导入模板.xlsx


BIN
fhKeeper/formulahousekeeper/management-platform/人员导入模板.xlsx


+ 11 - 1
fhKeeper/formulahousekeeper/octopus/src/routes.js

@@ -45,7 +45,7 @@ import simpleReport from './views/simplereport/list';
 
 // 研究中心
 // import research from './views/research/list';
-
+import migrateData from './views/migrateData/migrateData'
 // 角色权限
 import quanx from './views/quanx/quanx'
 
@@ -163,6 +163,16 @@ export const allRouters = [//组织架构
             { path: '/reviewImport', component: reviewImport, name: '操作记录' },
         ]
     },
+    {
+        path: '/',
+        component: Home,
+        name: '待办任务',
+        iconCls: 'iconfont firerock-iconshenhe',
+        leaf: true,
+        children: [
+            { path: '/migrateData', component: migrateData, name: '数据迁移' },
+        ]
+    },
     //成本统计
     // {
     //     path: '/',

+ 158 - 0
fhKeeper/formulahousekeeper/octopus/src/views/migrateData/migrateData.vue

@@ -0,0 +1,158 @@
+@<template>
+  <section>
+    <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
+        <el-form :inline="true">
+            <el-form-item label="数据迁移" style="margin-left:20px"></el-form-item>
+        </el-form>
+    </el-col>
+    <el-col :span="24" style="padding:25px">
+        <el-form style="width:500px;margin:0 auto" label-position="left" label-width="100px">
+            <el-form-item label="当前公司" style="margin-top:40px">
+                <el-input v-model="nowCompanyId" placeholder="请输入" clearable style="width:250px"></el-input>
+            </el-form-item>
+            <el-form-item label="当前公司超管" style="margin-top:40px">
+                <el-input v-model="nowCompanyCg" placeholder="请输入" clearable style="width:250px"></el-input>
+            </el-form-item>
+            <el-form-item label="目标公司" style="margin-top:40px">
+                <el-input v-model="toCompanyId" placeholder="请输入" clearable style="width:250px"></el-input>
+            </el-form-item>
+            <el-form-item label="目标公司超管" style="margin-top:40px">
+                <el-input v-model="toCompanyCg" placeholder="请输入" clearable style="width:250px"></el-input>
+            </el-form-item>
+        </el-form>
+        <div style="width:500px;margin:100px auto">
+            <div style="width:350px;text-align:center">
+                <el-button type="primary" @click="submit" style="margin:0 auto">立即迁移</el-button>
+            </div>
+        </div>
+    </el-col>
+  </section>
+</template>
+
+<script>
+export default {
+    data(){
+        return{
+            list:[],
+            nowCompanyId: '',
+            toCompanyId: '',
+            nowCompanyCg: '',
+            toCompanyCg: ''
+        }
+    },
+    mounted(){
+        // this.getList()
+    },
+    methods:{
+        getList() {
+                this.http.post('/company/getList', {
+                    pageIndex: 1,
+                    pageSize: 9999,
+                    companyName: '',
+                    isMeal: 0
+                },
+                res => {
+                    if (res.code == "ok") {
+                        this.list = res.data.records;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+        submit() {
+            this.nowCompanyId = this.nowCompanyId.trim()
+            this.nowCompanyCg = this.nowCompanyCg.trim()
+            this.toCompanyId = this.toCompanyId.trim()
+            this.toCompanyCg = this.toCompanyCg.trim()
+            if(this.nowCompanyId == ''){
+                this.$message({
+                    message: '当前公司未输入!',
+                    type: 'warning'
+                })
+                return
+            }else if(this.toCompanyId == ''){
+                this.$message({
+                    message: '目标公司未输入!',
+                    type: 'warning'
+                })
+                return
+            }else if(this.nowCompanyCg == ''){
+                this.$message({
+                    message: '当前公司超管未输入!',
+                    type: 'warning'
+                })
+                return
+            }else if(this.toCompanyCg == ''){
+                this.$message({
+                    message: '目标公司超管未输入!',
+                    type: 'warning'
+                })
+                return
+            }else if(this.nowCompanyId == this.toCompanyId){
+                this.$message({
+                    message: '当前公司与目标公司相同!',
+                    type: 'warning'
+                })
+                return
+            }
+            this.$confirm('确认进行数据迁移吗?','提示',{
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning'
+            }).then(()=>{
+                this.http.post('/company/dataMigration',{
+                    oldCompanyId: this.nowCompanyId,
+                    targetCompanyId: this.toCompanyId,
+                    oldUserName: this.nowCompanyCg,
+                    targetUserName: this.toCompanyCg
+                },res => {
+                    if(res.code == 'ok'){
+                        this.$message({
+                            message: '数据迁移成功',
+                            type: 'success'
+                        })
+                        this.nowCompanyId = null
+                        this.toCompanyId = null
+                        this.getList()
+                    }else {
+                        this.$message({
+                            message: res.msg,
+                            type: 'error'
+                        })
+                    }
+                },err => {
+                    this.$message({
+                        message: err,
+                        type: 'error'
+                    })
+                })
+            }).catch(()=>{
+                this.$message({
+                    message: '已取消操作',
+                    type: 'info'
+                })
+            })
+            
+        }
+    }
+}
+</script>
+
+<style scoped>
+.sidebars {
+  height: 100%;
+  position: absolute;
+  border-right: 1px solid #E6E6E6;
+  z-index: 2;
+  top: 0;
+}
+</style>

+ 10 - 5
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CompanyController.java

@@ -1,31 +1,31 @@
 package com.management.platform.controller;
 
 
-import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.management.platform.entity.Company;
 import com.management.platform.entity.CompanyDingding;
-import com.management.platform.entity.VcompanyCustomerContact;
 import com.management.platform.entity.WxCorpInfo;
 import com.management.platform.mapper.CompanyDingdingMapper;
 import com.management.platform.mapper.CompanyMapper;
 import com.management.platform.mapper.WxCorpInfoMapper;
 import com.management.platform.service.CompanyService;
 import com.management.platform.util.HttpRespMsg;
-import org.apache.el.parser.BooleanNode;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.*;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.util.StringUtils;
 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.client.RestTemplate;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Optional;
@@ -167,5 +167,10 @@ public class CompanyController {
             return error;
         }
     }
+    //数据迁移
+    @RequestMapping("/dataMigration")
+    public HttpRespMsg dataMigration(HttpServletRequest request, Integer oldCompanyId, Integer targetCompanyId,String oldUserName,String targetUserName){
+        return companyService.dataMigration(request,oldCompanyId,targetCompanyId,oldUserName,targetUserName);
+    }
 }
 

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

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2022-06-20
+ */
+@RestController
+@RequestMapping("/report")
+public class ReportController {
+
+}
+

+ 40 - 49
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/entity/Report.java

@@ -8,15 +8,9 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import java.time.LocalDateTime;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
-import java.util.HashMap;
-import java.util.List;
-
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.management.platform.entity.vo.WorktimeItem;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
-import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * <p>
@@ -24,12 +18,13 @@ import org.springframework.format.annotation.DateTimeFormat;
  * </p>
  *
  * @author Seyason
- * @since 2022-03-13
+ * @since 2022-06-20
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
 @Accessors(chain = true)
 public class Report extends Model<Report> {
+
     private static final long serialVersionUID=1L;
 
     /**
@@ -54,8 +49,6 @@ public class Report extends Model<Report> {
      * 日期
      */
     @TableField("create_date")
-    @DateTimeFormat(pattern = "yyyy-MM-dd")
-    @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDate createDate;
 
     /**
@@ -65,13 +58,13 @@ public class Report extends Model<Report> {
     private Double workingTime;
 
     /**
-     * 报告内容
+     * 报告内容; 根据multi_worktime的取值,形式有差别
      */
     @TableField("content")
     private String content;
 
     /**
-     * 审查状态 0-未审核 1-已通过 2-未通过
+     * 审查状态 0-未审核 1-已通过 2-未通过, -1-导入待审核, 3草稿
      */
     @TableField("state")
     private Integer state;
@@ -136,54 +129,30 @@ public class Report extends Model<Report> {
     @TableField("progress")
     private Integer progress;
 
-
-    @TableField(exist = false)
-    private List<SubProject> subProjectList;
-    @TableField(exist = false)
-    private List<UserRecentTask> taskList;
-    @TableField(exist = false)
-    private List<ReportProfessionProgress> professionProgressList;
-    @TableField(exist = false)
-    private List<Stages> stages;
-
     /**
      * 部门审核状态: -1 专业未审核,0-部门未审核,1-已通过,2-未通过
      */
     @TableField("department_audit_state")
     private Integer departmentAuditState;
 
-
     /**
      * 阶段/岗位/工序
      */
     @TableField("stage")
     private String stage;
 
-
     /**
      * 图片的数组字符串
      */
     @TableField("pic_str")
     private String picStr;
 
-
-    @TableField(exist = false)
-    private String picAdd;
-
-    /**
-     * 传给客户端的图片数组
-     */
-    @TableField(exist = false)
-    private List<String> pics;
     /**
      * 是否是多个时间工作事项
      */
     @TableField("multi_worktime")
     private Integer multiWorktime;
 
-
-    @TableField(exist = false)
-    private List<WorktimeItem> worktimeList;
     /**
      * 驳回原因
      */
@@ -202,16 +171,12 @@ public class Report extends Model<Report> {
     @TableField("reject_userid")
     private String rejectUserid;
 
-
     /**
      * 选择的自定义维度
      */
     @TableField("degree_id")
     private Integer degreeId;
 
-
-    @TableField(exist = false)
-    private List<HashMap> degreeList;
     /**
      * 公司id
      */
@@ -247,24 +212,19 @@ public class Report extends Model<Report> {
      */
     @TableField("is_final_audit")
     private Integer isFinalAudit;
+
     /**
      * 审核流程:审核过程中,项目的审核状态; 0-待审核,1-审核通过
      */
     @TableField("project_audit_state")
     private Integer projectAuditState;
 
-
     /**
      * 任务分组id
      */
     @TableField("group_id")
     private Integer groupId;
 
-    @TableField(exist = false)
-    private String groupName;
-
-    @TableField(exist = false)
-    private List<TaskGroup> taskGroups;
     /**
      * 自定义的数值
      */
@@ -277,11 +237,42 @@ public class Report extends Model<Report> {
     @TableField("project_auditor_id")
     private String projectAuditorId;
 
-    @TableField(exist = false)
-    private List<ProjectAuditor> auditUserList;
+    /**
+     * 工时日报所属部门id
+     */
+    @TableField("dept_id")
+    private Integer deptId;
+
+    /**
+     * 加班时长
+     */
+    @TableField("overtime_hours")
+    private Double overtimeHours;
+
+    /**
+     * 加班薪资
+     */
+    @TableField("overtime_cost")
+    private BigDecimal overtimeCost;
+
+    /**
+     * 自定义文本信息内容
+     */
+    @TableField("custom_text")
+    private String customText;
+
+    /**
+     * 对应成本项的id
+     */
+    @TableField("basecost_id")
+    private Integer basecostId;
+
+    /**
+     * 项目日报审核时间
+     */
+    @TableField("project_audit_time")
+    private LocalDateTime projectAuditTime;
 
-    @TableField(exist = false)
-    private String projectAuditorName;
 
     @Override
     protected Serializable pkVal() {

+ 19 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/mapper/CustomerInfoMapper.java

@@ -0,0 +1,19 @@
+package com.management.platform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.CustomerInfo;
+
+import java.util.List;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-07-31
+ */
+public interface CustomerInfoMapper extends BaseMapper<CustomerInfo> {
+
+    public List<CustomerInfo> getAll(Integer companyId);
+}

+ 16 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/mapper/ProjectAuditorMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.management.platform.entity.ProjectAuditor;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2022-03-13
+ */
+public interface ProjectAuditorMapper extends BaseMapper<ProjectAuditor> {
+
+}

+ 4 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/CompanyService.java

@@ -4,6 +4,8 @@ import com.management.platform.entity.Company;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.management.platform.util.HttpRespMsg;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * <p>
  *  服务类
@@ -21,4 +23,6 @@ public interface CompanyService extends IService<Company> {
     HttpRespMsg setExpDate(Integer companyId, String date);
 
     HttpRespMsg setPackageList(Company company);
+
+    HttpRespMsg dataMigration(HttpServletRequest request,Integer oldCompanyId, Integer targetCompanyId,String oldUserName,String targetUserName);
 }

+ 16 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/ReportService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.Report;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2022-06-20
+ */
+public interface ReportService extends IService<Report> {
+
+}

+ 185 - 7
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/impl/CompanyServiceImpl.java

@@ -1,20 +1,24 @@
 package com.management.platform.service.impl;
 
-import com.management.platform.entity.Company;
-import com.management.platform.entity.OperationLog;
-import com.management.platform.entity.User;
-import com.management.platform.mapper.CompanyMapper;
-import com.management.platform.mapper.OperationLogMapper;
-import com.management.platform.mapper.UserMapper;
-import com.management.platform.service.CompanyService;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.api.R;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.*;
+import com.management.platform.service.*;
 import com.management.platform.util.HttpRespMsg;
+import com.mysql.cj.util.StringUtils;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -34,6 +38,22 @@ public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> impl
     HttpServletRequest request;
     @Resource
     UserMapper userMapper;
+    @Resource
+    ProjectMapper projectMapper;
+    @Resource
+    ReportMapper reportMapper;
+    @Resource
+    ReportService reportService;
+    @Resource
+    CustomerInfoMapper customerInfoMapper;
+    @Resource
+    ParticipationMapper participationMapper;
+    @Resource
+    DepartmentMapper departmentMapper;
+    @Resource
+    ProjectAuditorMapper projectAuditorMapper;
+    @Resource
+    UserService userService;
 
     @Override
     public HttpRespMsg addMembCount(Integer companyId, int addCount) {
@@ -115,4 +135,162 @@ public class CompanyServiceImpl extends ServiceImpl<CompanyMapper, Company> impl
         saveLog(str);
         return new HttpRespMsg();
     }
+
+    @Override
+    @Transactional
+    public HttpRespMsg dataMigration(HttpServletRequest request,Integer oldCompanyId, Integer targetCompanyId,String oldUserName,String targetUserName) {
+        HttpRespMsg msg=new HttpRespMsg();
+        User oldManager = userMapper.selectOne(new QueryWrapper<User>().eq("name", oldUserName).eq("company_id",oldCompanyId));
+        User targetManager = userMapper.selectOne(new QueryWrapper<User>().eq("name", targetUserName).eq("company_id",targetCompanyId));
+        if(oldManager==null){
+            msg.setError("管理员["+oldUserName+"]不存在");
+            return msg;
+        }
+        if(targetManager==null){
+            msg.setError("管理员["+targetManager+"]不存在");
+            return msg;
+        }
+        if(!oldManager.getRoleName().equals("超级管理员")){
+            msg.setError("原公司超级管理员不正确");
+            return msg;
+        }
+        if(!targetManager.getRoleName().equals("超级管理员")){
+            msg.setError("目标公司超级管理员不正确,当前角色是:"+targetManager.getRoleName());
+            return msg;
+        }
+        List<User> oldUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", oldCompanyId));
+        List<User> targetUserList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", targetCompanyId));
+        List<Department> oldDepartmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", oldCompanyId));
+        List<Department> targetDepartmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", targetCompanyId));
+        //处理部门负责人
+        List<User> updateUserList=new ArrayList<>();
+        for (Department oldDepartment : oldDepartmentList) {
+            for (Department targetDepartment : targetDepartmentList) {
+                if(oldDepartment.getDepartmentName().equals(targetDepartment.getDepartmentName())){
+                    if(oldDepartment.getManagerId()!=null){
+                        Optional<User> oldUser = oldUserList.stream().filter(ou -> ou.getId().equals(oldDepartment.getManagerId())).findFirst();
+                        Optional<User> targetUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(oldUser.get().getDingdingUserid())).findFirst();
+                        targetDepartment.setManagerId(targetUser.get().getId());
+                        targetUser.get().setDepartmentId(targetDepartment.getDepartmentId());
+                        targetUser.get().setDepartmentName(targetDepartment.getDepartmentName());
+                        updateUserList.add(targetUser.get());
+                        departmentMapper.updateById(targetDepartment);
+                    }
+                }
+            }
+        }
+        List<String> ids=new ArrayList<>();
+        //获取在目标公司也存在的人员
+        for (User targetUser : targetUserList) {
+            for (User oldUser : oldUserList) {
+                if(targetUser.getDingdingUserid().equals(oldUser.getDingdingUserid())){
+                    ids.add(oldUser.getId());
+                }
+            }
+        }
+        List<Participation> participationList = participationMapper.selectList(new QueryWrapper<Participation>().in("user_id", ids));
+        //取项目id  按照项目参与人
+        List<Integer> projectIds = participationList.stream().map(pc -> pc.getProjectId()).collect(Collectors.toList());
+        List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", projectIds));
+        List<Project> targetProjectList = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", targetCompanyId));
+        List<String> list = targetProjectList.stream().map(tp -> tp.getProjectCode()).collect(Collectors.toList());
+        List<ProjectAuditor> oldProjectAuditorList = projectAuditorMapper.selectList(new QueryWrapper<ProjectAuditor>().in("project_id", projectIds));
+        //处理相关项目
+        for (Project pro: projectList) {
+            //此前的项目负责人不存在当前组织架构
+            Optional<User> inchargerUser = oldUserList.stream().filter(ou -> ou.getId().equals(pro.getInchargerId())).findFirst();
+            boolean flag = targetUserList.stream().anyMatch(tu -> tu.getDingdingUserid().equals(inchargerUser.get().getDingdingUserid()));
+            if(!flag){
+                msg.setError("当前项目["+pro.getProjectName()+"]负责人["+inchargerUser.get().getName()+"]不存在");
+                return msg;
+            }else {
+                Optional<User> targetInchargerUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(inchargerUser.get().getDingdingUserid())).findFirst();
+                pro.setInchargerId(targetInchargerUser.get().getId());
+            }
+            /*//在新公司创建相同的客户信息
+            CustomerInfo customerInfo = customerInfoMapper.selectById(pro.getCustomerId());
+            customerInfo.setId(null);
+            customerInfo.setCompanyId(targetCompanyId);
+            customerInfoMapper.insert(customerInfo);
+            pro.setCustomerId(customerInfo.getId());*/
+            List<ProjectAuditor> projectAuditorList = oldProjectAuditorList.stream().filter(pa -> pa.getProjectId().equals(pro.getId())).collect(Collectors.toList());
+            Optional<User> creatorUser = oldUserList.stream().filter(ou -> ou.getName().equals("李婷婷")).findFirst();
+            Optional<User> targetCreatorUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(creatorUser.get().getDingdingUserid())).findFirst();
+            pro.setCreatorId(targetCreatorUser.get().getId());
+            //当前项目的相关参与人
+            List<String> userList = participationList.stream().filter(pc -> pc.getProjectId().equals(pro.getId())).collect(Collectors.toList()).stream().map(o -> o.getUserId()).collect(Collectors.toList());
+            pro.setId(null);
+            pro.setCompanyId(targetCompanyId);
+            if(list.contains(pro.getProjectCode())){
+                continue;
+            }
+            projectMapper.insert(pro);
+            //处理项目审核人
+            for (ProjectAuditor projectAuditor : projectAuditorList) {
+                projectAuditor.setProjectId(pro.getId());
+                projectAuditor.setId(null);
+                Optional<User> first = targetUserList.stream().filter(tu -> tu.getName().equals(projectAuditor.getAuditorName())).findFirst();
+                if(first.isPresent()){
+                    projectAuditor.setAuditorId(first.get().getId());
+                    projectAuditor.setAuditorName(first.get().getName());
+                    projectAuditorMapper.insert(projectAuditor);
+                }
+            }
+            for(String s:userList){
+                Participation participation=new Participation();
+                participation.setProjectId(pro.getId());
+                Optional<User> user = oldUserList.stream().filter(ou -> ou.getId().equals(s)).findFirst();
+                Optional<User> targetUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(user.get().getDingdingUserid())).findFirst();
+                participation.setUserId(targetUser.get().getId());
+                participationMapper.insert(participation);
+            }
+        }
+        List<Project> oldProjectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", projectIds));
+        //处理日报
+        List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().in("creator_id", ids));
+        List<Report> updateReportList = new ArrayList<>();
+        for (Report report : reportList) {
+            //项目关联
+
+            Optional<Project> oldProject = oldProjectList.stream().filter(op -> op.getId().equals(report.getProjectId())).findFirst();
+            Optional<Project> targetProject = projectList.stream().filter(pro -> pro.getProjectCode().equals(oldProject.get().getProjectCode())).findFirst();
+            report.setProjectId(targetProject.get().getId());
+            Optional<User> oldUser = oldUserList.stream().filter(ou -> ou.getId().equals(report.getCreatorId())).findFirst();
+            Optional<User> targetUser = targetUserList.stream().filter(tu -> tu.getDingdingUserid().equals(oldUser.get().getDingdingUserid())).findFirst();
+            report.setCreatorId(targetUser.get().getId());
+            //部门数据
+            Optional<Department> oldDepart = oldDepartmentList.stream().filter(od -> od.getDepartmentId().equals(report.getDeptId())).findFirst();
+            Optional<Department> targetDepart = targetDepartmentList.stream().filter(td -> td.getDepartmentName().equals(oldDepart.get().getDepartmentName())).findFirst();
+            report.setDeptId(targetDepart.get().getDepartmentId());
+            //当前审核部门数据
+            Optional<Department> oldAuditDepart = oldDepartmentList.stream().filter(od -> od.getDepartmentId().equals(report.getAuditDeptid())).findFirst();
+            if(oldAuditDepart.isPresent()){
+                Optional<Department> targetAuditDepart =targetDepartmentList.stream().filter(td -> td.getDepartmentName().equals(oldAuditDepart.get().getDepartmentName())).findFirst();
+                report.setAuditDeptid(targetAuditDepart.get().getDepartmentId());
+                report.setAuditDeptManagerid(targetAuditDepart.get().getManagerId());
+            }
+            //是否被驳回
+            if(!StringUtils.isNullOrEmpty(report.getRejectUsername())){
+                Optional<User> rejectUser = targetUserList.stream().filter(tu -> tu.getName().equals(report.getRejectUsername())).findFirst();
+                report.setRejectUserid(rejectUser.get().getId());
+            }
+            report.setCompanyId(targetCompanyId);
+//            reportMapper.updateById(report);
+            Report newReport = new Report();
+            newReport.setId(report.getId());
+            newReport.setCompanyId(targetCompanyId);
+            newReport.setProjectId(report.getProjectId());
+            newReport.setCreatorId(report.getCreatorId());
+            newReport.setDeptId(report.getDeptId());
+            newReport.setAuditDeptid(report.getAuditDeptid());
+            newReport.setAuditDeptManagerid(report.getAuditDeptManagerid());
+            newReport.setRejectUserid(report.getRejectUserid());
+            updateReportList.add(newReport);
+        }
+        //改成批量更新
+        if (updateReportList.size() > 0) {
+            reportService.updateBatchById(updateReportList);
+        }
+        return msg;
+    }
 }

+ 20 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.Report;
+import com.management.platform.mapper.ReportMapper;
+import com.management.platform.service.ReportService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2022-06-20
+ */
+@Service
+public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
+
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 7 - 477
fhKeeper/formulahousekeeper/ops-platform/src/main/resources/mapper/ReportMapper.xml


+ 2 - 0
fhKeeper/formulahousekeeper/timesheet/src/permissions.js

@@ -50,6 +50,7 @@ const StringUtil = {
         reportCostWarning: false, // 工时成本预警表 //
         reportPhaseCost: false, // 查看阶段成本 //
         reportTimeDivide: false, // 人员工时分配表 //
+        reportTimely: false, // 查看人员填报及时率 //
 
         // 请假模块
         leaveFil : false, // 请假填报 // 
@@ -135,6 +136,7 @@ const StringUtil = {
         arr[i] == '查看阶段成本' ? obj.reportPhaseCost = true : ''
         arr[i] == '查看人员工时分配' ? obj.reportTimeDivide = true : ''
         arr[i] == '自定义配置' ? obj.structureCustomConfig = true : ''
+        arr[i] == '查看人员填报及时率' ? obj.reportTimely = true : ''
     }
 
     return obj

+ 25 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/centerManage/centerManage.vue

@@ -8,6 +8,7 @@
                 <el-link icon="el-icon-circle-plus-outline" type="primary" :underline="false" class="tanjia" @click="addNewSubProject()">添加{{user.timeType.customDegreeName}}</el-link>
                 <el-link type="primary" :underline="false" @click="batchDelete" class="tanjia">批量删除{{user.timeType.customDegreeName}}</el-link> -->
                 <el-link type="primary" :underline="false" @click="batchDelete" class="tanjia">批量删除</el-link>
+                <el-link type="primary" :underline="false" @click="outoCenterRatio" class="tanjia">数据导出</el-link>
                 <el-link type="primary" :underline="false" @click="intoCenterRatio" class="tanjia">批量导入</el-link>
                 <el-link icon="el-icon-circle-plus-outline" type="primary" :underline="false" class="tanjia" @click="addNewSubProject()">新增</el-link>
 
@@ -140,6 +141,30 @@
             intoCenterRatio(){
                 this.intoCenterDialog=true;
             },
+            outoCenterRatio(){
+                this.http.post('/report-extra-degree/exportData',{},
+                res => {
+                    if(res.code == 'ok'){
+                        let filePath = res.data;
+                        let fileName = filePath.split('/upload/')[1]
+                        const a = document.createElement('a'); // 创建a标签
+                        a.setAttribute('download', fileName);// download属性
+                        a.setAttribute('href', filePath);// href链接
+                        a.click(); //自执行点击事件
+                        a.remove();
+                    }else{
+                        this.$message({
+                            message: res.msg,
+                            type: 'error'
+                        })
+                    }
+                },err => {
+                    this.$message({
+                        message: err,
+                        type: 'error'
+                    })
+                })
+            },
             batchImportData(item) {
                 //首先判断文件类型
                 let str = item.file.name.split(".");

+ 146 - 24
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -26,6 +26,8 @@
                 <el-menu-item index="1-6" v-if="user.company.packageCustomer == 1 && permissions.reportProfits"><p @click="ssl(4)">客户项目利润表</p></el-menu-item>
                 <el-menu-item index="1-7" v-if="permissions.reportPhase"><p @click="ssl(5)">项目阶段工时表</p></el-menu-item>
                 <el-menu-item index="1-8" v-if="permissions.reportOvertime"><p @click="ssl(6)">加班情况统计表</p></el-menu-item>
+
+                <el-menu-item index="1-10" v-if="permissions.reportTimely"><p @click="ssl(9)">员工填报及时率</p></el-menu-item>
               </el-submenu>
             </el-menu>
         </el-col>
@@ -42,7 +44,7 @@
   <div class="contents">
     <div class="headine" ref="headine">
       <h3 ref="headHe" style="padding-left: 210px">{{shuz[ins]}}</h3>
-      <div class="headScreen" v-if="ins != 6 && ins != 8">
+      <div class="headScreen">
       <!-- 客户项目利润表的筛选 -->
         <template v-if="ins == 4">
           <el-select v-model="customerId" placeholder="请选择客户" clearable filterable size="small" @change="selcts(4)" style="margin-right:20px">
@@ -56,25 +58,29 @@
             </el-option>
           </el-select>
         </template>
-
-        <el-select v-if="ins != 4" v-model="proJuctId" placeholder="请选择项目" clearable filterable size="small" @change="selcts()">
+        <!-- 时间段筛选 -->
+          <template v-if="ins == 6 || ins == 8 || ins == 9">
+            <span class="demonstration" style="color:#999;padding:0 10px">时间段</span>
+            <el-date-picker v-model="rangeDatas" type="daterange" value-format="yyyy-MM-dd" placeholder="选择开始日期" @change="picks()" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width:350px" :clearable="false" size="small"> </el-date-picker>
+          </template>
+          
+        <!-- 项目筛选 -->
+        <el-select v-if="ins != 4 && ins != 8 && ins != 9" v-model="proJuctId" placeholder="请选择项目" clearable filterable size="small" @change="selcts()" style="margin-left:10px">
           <el-option v-for="(item) in proListOvertime" :key="item.id" :label="item.projectName + (item.projectCode ? item.projectCode : '')" :value="item.id">
             <span style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
             <span style="float: right;font-size: 13px;margin-left: 20px">{{ item.projectName }}</span>
           </el-option>
         </el-select>
-      </div>
-      <div v-if="ins == 6 || ins == 8" class="headScreen">
-          <el-select v-if="ins == 6" v-model="proJuctId" placeholder="请选择项目" @change="selcts()" clearable filterable size="small">
-            <el-option v-for="(item, index) in proListOvertime" :key="index" :label="item.projectName + (item.projectCode ? item.projectCode : '')" :value="item.id">
-              <span style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
-              <span style="float: right;font-size: 13px;margin-left: 20px">{{ item.projectName }}</span>
-            </el-option>
-          </el-select>
-          <span class="demonstration" style="color:#999;padding:0 10px">时间段</span>
-          <el-date-picker v-model="rangeDatas" type="daterange" value-format="yyyy-MM-dd" placeholder="选择开始日期" @change="picks()" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width:350px" :clearable="false" size="small"> </el-date-picker>
-          <el-select v-model="userId" placeholder="请选择人员" @change="selcts()" clearable filterable size="small" style="margin-left:10px">
-            <el-option v-for="(item, index) in userList" :key="index" :label="item.name" :value="item.id"></el-option>
+          
+          <!-- 部门筛选 -->
+          <el-cascader v-if="ins == 9" v-model="departmentIdArray" :options="departmentList" placeholder="请选择部门"
+            :props="{ checkStrictly: false,expandTrigger: 'hover' }" :show-all-levels="false" clearable
+            @change="selcts(9)" size="small" style="margin-left:10px"
+          ></el-cascader>
+
+          <!-- 人员筛选 -->
+          <el-select v-if="ins == 6 || ins == 8 || ins == 9" v-model="userId" placeholder="请选择人员" @change="selcts()" clearable filterable size="small" style="margin-left:10px">
+            <el-option v-for="(item, index) in selUserList" :key="index" :label="item.name" :value="item.id"></el-option>
           </el-select>
       </div>
       <p style="float: right;margin-right: 25px;" ><el-button type="primary" @click="exportExcel" size="mini">报表导出</el-button></p>
@@ -417,6 +423,12 @@
 
             </el-table>
 
+            <el-table v-if="ins == 9" key="9" border :data="timelyCostList" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
+                <el-table-column prop="userName" label="人员" min-width="200" align="center"></el-table-column>
+                <el-table-column prop="departmentName" label="所属部门" min-width="200" align="center"></el-table-column>
+                <el-table-column prop="timelinessRate" label="填报及时率" min-width="200" align="center"></el-table-column>
+            </el-table>
+
             
 
         <!--工具条-->
@@ -596,7 +608,10 @@ export default {
       z   : null,
       value: null,
       dialog: false, // 单据查看展示
-      shuz: ["项目报表","项目任务报表","项目成本报表","项目收支平衡表(利润表)","客户项目利润报表","项目阶段工时表","加班情况统计表","工时成本预警表","人员工时分配表"],
+      shuz: ["项目报表","项目任务报表","项目成本报表",
+      "项目收支平衡表(利润表)","客户项目利润报表","项目阶段工时表",
+      "加班情况统计表","工时成本预警表","人员工时分配表",
+      "员工填报及时率报表"],
       ins: 0,
       user: JSON.parse(sessionStorage.user),
       overTimeList: [], // 项目加班情况统计列表
@@ -617,7 +632,11 @@ export default {
       cusProJuctId: '',
       cusProListOvertime: [],
 
-      userWorkHoursList: []
+      userWorkHoursList: [],
+      timelyCostList: [],
+      departmentList: [],
+      departmentIdArray: [],
+      selUserList: []
 
     };
   },
@@ -636,6 +655,7 @@ export default {
     this.getProjectListOvertime()
     this.getProjectList();
     this.getUserList()
+    this.getDepartmentList()
     this.getcustomerList()
     this.getcusProjectList()
   },
@@ -702,6 +722,7 @@ export default {
       res => {
           if (res.code == "ok") {
               this.userList = res.data.records;
+              this.selUserList = this.userList
           } else {
               this.$message({
                 message: res.msg,
@@ -716,6 +737,45 @@ export default {
           });
       });
     },
+    getDepartmentList() {
+      this.http.post( this.port.manage.depList, {},
+        res => {
+          if (res.code == "ok") {
+            let dptlist = JSON.parse(JSON.stringify(res.data));
+            this.departmentList = this.changeArr(dptlist);
+          } else {
+            this.$message({
+              message: res.msg,
+              type: "error"
+            });
+          }
+        },error => {
+          this.$message({
+            message: error,
+            type: "error"
+          });
+        });
+    },
+    changeArr(arr) {
+                for (var i = 0; i < arr.length; i++) {
+                    if(arr[i].id != -1 && arr[i].id != 0) {
+                        if (arr[i].children != null && arr[i].children.length>0) {
+                            arr[i].children = this.changeArr(arr[i].children);
+                        }
+                        arr[i].id && (arr[i].value = arr[i].id);
+                        delete arr[i].id;
+                    }
+                }
+                for(var i in arr) {
+                    if(arr[i].id == -1 || arr[i].id == 0) {
+                        arr.splice(i,1)
+                    }    
+                }
+                return arr;
+            },
+
+
+
     getSummaries(param) {
         const { columns, data } = param;
         const sums = [];
@@ -812,6 +872,8 @@ export default {
                     // this.$refs.eltable7.doLayout()
                 } else if (this.ins == 8) {
                     this.getWorkHoursList()
+                } else if (this.ins == 9) {
+                    this.getTimelyCostList()
                 }
             },
       exportExcel() {
@@ -850,6 +912,11 @@ export default {
           url += "/exportUserWorkingTimeList"
           sl.startDate = this.rangeDatas[0]
           sl.endDate = this.rangeDatas[1]
+        } else if (this.ins == 9) {
+          fName = "人员填报及时率统计.xls"
+          url += "/exportReportTimelinessRate"
+          sl.startDate = this.rangeDatas[0]
+          sl.endDate = this.rangeDatas[1]
         }
           this.http.post(url, sl,
             res => {
@@ -892,6 +959,9 @@ export default {
       this.overTimeList = []
       this.baseCostList = []
       this.userWorkHoursList = []
+      this.timelyCostList = []
+      this.departmentIdArray = [],
+      this.userId = null
       this.getList();
     },
     getCustomerProjectInAndOut() {
@@ -1137,6 +1207,40 @@ export default {
         })
       })
     },
+    getTimelyCostList(){
+      let parameter = {
+        startDate: this.rangeDatas[0],
+        endDate: this.rangeDatas[1],
+        pageIndex: this.page,
+        pageSize: this.size,
+      }
+      if(this.userId){
+        parameter.userId = this.userId
+      }
+      if(this.departmentIdArray.length != 0){
+        parameter.departmentId = this.departmentIdArray[this.departmentIdArray.length - 1]
+      }
+      this.listLoading = true
+      this.http.post('/project/getUserReportTimelinessRate',parameter,
+        res => {
+          this.listLoading = false
+          if(res.code == 'ok'){
+            this.timelyCostList = res.data.list
+            this.total = res.data.total
+          }else {
+            this.$message({
+              message: res.msg,
+              type: 'error'
+            })
+          }
+        },err => {
+          this.listLoading = false
+          this.$message({
+            message: err,
+            type: 'error'
+          })
+        })
+    },
 
     budgetFilter(emList,eId){
                 let emItem = emList.filter((em)=>{
@@ -1162,7 +1266,11 @@ export default {
       let budget = this.budgetFilter(row.curcostList,eid)
       let already = this.AlreadyFilter(row.realcostList,eid)
       let ret = (already / budget)*100
-      return ret.toFixed(0)
+      if(budget == 0){
+        return "NaN"
+      }else{
+        return ret.toFixed(0)
+      }
     },
 
     
@@ -1287,17 +1395,31 @@ export default {
       if(this.ins == 8){
         this.getWorkHoursList()
       }
+      if(this.ins == 9){
+        this.getTimelyCostList()
+      }
     },
     // 日期
     getCurrentRangeTime() {
         var _this = this;
-      let yy = new Date().getFullYear();
-      let mm = new Date().getMonth()+1;
-      let dd = new Date().getDate();
-      _this.gettime = [yy+'-'+mm+'-'+'01' , yy +'-'+ mm +'-'+ dd];
-       return  _this.gettime
+        let yy = new Date().getFullYear();
+        let mm = new Date().getMonth()+1;
+        let dd = new Date().getDate();
+        let time1 = yy + '-' + (mm < 10 ? '0' + mm : mm) + '-' + '01'
+        let time2 = yy + '-' + (mm < 10 ? '0' + mm : mm) + '-' + (dd < 10 ? '0' + dd : dd)
+        _this.gettime = [time1 , time2];
+        return  _this.gettime
     },
-    selcts() {
+    selcts(e) {
+      if(e == 9){
+        this.userId = null
+        if(this.departmentIdArray.length != 0){
+          let deptid = this.departmentIdArray[this.departmentIdArray.length - 1]
+          this.selUserList = this.userList.filter(item => item.departmentId == deptid)
+        }else{
+          this.selUserList = this.userList
+        }
+      }
       // console.log(this.customerId);
       this.getList()
     },

+ 92 - 8
fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue

@@ -50,6 +50,7 @@
     <div v-if="!displayTable" class="headine" ref="headine">
       <h3 ref="headHe" style="padding-left: 220px">{{shuz[ins]}}</h3>
       <p style="float: right;margin-right: 25px;"><el-button type="primary" @click="submits" size="mini">提交</el-button></p>
+      <p style="float: right;margin-right: 25px;"><el-button type="primary" @click="submitUpload" size="mini">批量上传</el-button></p>
     </div>
     <!-- 上面部分 -->
     <div ref="staff" style="margin: 20px 20px 0 220px; width: 81.5%" >
@@ -238,7 +239,7 @@
               <el-table-column prop="code" label="票据编号"></el-table-column>
               <el-table-column prop="totalAmount" label="金额(元)" align="center">
                 <template slot-scope="scope" >
-                  <span style="float:right;margin-right:20px">{{scope.row.totalAmount.toFixed(2)}}</span>
+                  <span style="float:right;margin-right:20px">{{scope.row.totalAmount ? scope.row.totalAmount.toFixed(2) : '0'}}</span>
                 </template>
               </el-table-column>
               <el-table-column prop="ownerName" label="报销人" ></el-table-column>
@@ -442,6 +443,23 @@
           <el-button v-else type="primary" @click="dialog = false">确 定</el-button>
         </span>
       </el-dialog>
+
+      <!-- 批量上传 -->
+      <el-dialog title="批量上传" v-if="importDialog" :visible.sync="importDialog" customClass="customWidth" width="500px">
+            <p>1. 下载
+            <el-link type="primary" style="margin-left:5px;" :underline="false" href="./upload/费用报销导入模板.xlsx" download="费用报销导入模板.xlsx">费用报销导入模板.xlsx</el-link>
+            </p>
+            <p>2. 填写excel模板,并上传。</p>
+            <div style="padding-bottom:1em;">
+              <div style="width:300px;margin:0 auto">
+              <el-upload ref="upload" action="#" :auto-upload="false" multiple :on-change="fileListAdd" :on-remove="fileListDel">
+                <el-button slot="trigger" size="small" type="primary" style="">选取文件</el-button>
+                <el-button type="primary" size="small" style="margin-left:30px" :underline="false" :loading="importingData" @click="submitUploadSure" :disabled="fileList.length == 0">开始上传</el-button>
+                <el-button size="small" style="margin-left:30px" @click="fileListClear">清空</el-button>
+              </el-upload>
+              </div>
+            </div>
+        </el-dialog>
   </section>
 </template>
 
@@ -452,6 +470,7 @@ export default {
   props: {},
   data() {
     return {
+      fileList: [],
       muHeight: document.documentElement.clientHeight || document.body.clientHeight,
       imgs: [],
       addFormRules: {ownerId: [{ required: true, message: "请选择报销人", trigger: "blur" }]},      
@@ -497,7 +516,9 @@ export default {
       shuz: ['一般费用填报', '差旅费用填报', '外包费用填报'],
       ins: 0,
       apl: '',
-      diz: 'http://worktime.ttkuaiban.com'
+      diz: 'http://worktime.ttkuaiban.com',
+      importDialog: false,
+      importingData: false
     };
   },
   computed: {
@@ -545,6 +566,69 @@ export default {
       }
   },
   methods: {
+    submitUpload(){
+      this.importDialog = true
+    },
+    fileListAdd(file,fileList){
+      let str = file.name.split(".");
+      let format = str[str.length - 1];
+      if (format != "xls" && format != "xlsx") {
+        this.$message({
+          message: "请选择.xls或.xlsx文件",
+          type: "error"
+        });
+        fileList.splice(fileList.length-1)
+      }
+      this.fileList = fileList
+    },
+    fileListDel(file,fileList){
+      this.fileList = fileList
+    },
+    fileListClear(){
+      this.$refs.upload.clearFiles();
+      this.fileList = []
+    },
+    submitUploadSure(){
+        this.$confirm('确认上传?','提示',{
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'success'
+        }).then(()=>{
+          let formData = new FormData();
+          for(let i in this.fileList){
+            formData.append("files", this.fileList[i].raw);
+          }
+          this.importingData = true
+          // console.log(formData,this.fileList);
+          // return
+          this.http.uploadFile( '/expense-sheet/importData', formData,
+          res => {
+              if (res.code == "ok") {
+                  this.importingData = false
+                  this.$refs.upload.clearFiles();
+                  this.fileList = []
+                  this.$message({
+                      message: "上传成功",
+                      type: "success"
+                  });
+              } else {
+                  this.importingData = false
+                  this.$message({
+                      message: res.msg,
+                      type: "error"
+                  });
+              }
+          },error => {
+              this.importingData = false
+              this.$message({
+                  message: error,
+                  type: "error"
+              });
+          });
+        }).catch(()=>{})
+                    
+                
+    },
     restrictNumber(targetId) {
         let inpu = document.getElementById(targetId);
         inpu.value = inpu.value.replace(/[^\d.]/g, "");  //仅保留数字和"."
@@ -770,7 +854,7 @@ export default {
     },
     //获取单据列表
     getList() {
-      console.log(this.date)
+      // console.log(this.date)
       var stat = ''
       var end = ''
       if(this.date) {
@@ -1016,20 +1100,20 @@ export default {
       }, 0)
     },
     zhi(e) {
-      console.log('看看值', e)
+      // console.log('看看值', e)
       var i = e
       if(this.invoiceList[i].amount == null || this.invoiceList[i].amount == 'null' || this.invoiceList[i].taxPercent == null || this.invoiceList[i].taxPercent == 'null') {
         return
       }
       this.invoiceList[i].taxValue = this.invoiceList[i].amount * this.invoiceList[i].taxPercent / 100
-      console.log(this.invoiceList[i].amount, this.invoiceList[i].taxPercent)
+      // console.log(this.invoiceList[i].amount, this.invoiceList[i].taxPercent)
       var shui =  this.invoiceList[i].taxPercent / 100 // 税率
       var zhi = this.invoiceList[i].amount / (1 + shui) * shui
       this.invoiceList[i].taxValue = zhi.toFixed(2)
     },
     zhiNum(i, val) {
-      console.log('看看值', i, val)
-      console.log(this.ParticularsList.invoiceList)
+      // console.log('看看值', i, val)
+      // console.log(this.ParticularsList.invoiceList)
       if(this.ParticularsList.invoiceList[i].amount == null || this.ParticularsList.invoiceList[i].amount == 'null' || this.ParticularsList.invoiceList[i].taxPercent == null || this.ParticularsList.invoiceList[i].taxPercent == 'null') {
         return
       }
@@ -1183,7 +1267,7 @@ export default {
           
         });
 
-        console.log(sums, 123)
+        // console.log(sums, 123)
         this.$nextTick(()=>{ this.$refs.tab.doLayout()})
         return sums;
       }

+ 35 - 33
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -35,8 +35,8 @@
                 </div>
                 <div style="display:flex;">
                 <div v-if="permissions.reportsCompany || user.manageDeptId != 0 || permissions.reportsDept" :style="'overflow-x:hidden;overflow-y:auto;height:' + tableHeight + 'px;'">
-                    <div style="width:205px;">
-                        <el-select v-model="selectState" size="small" @change="stateChange" >
+                    <div style="width:240px;">
+                        <el-select v-model="selectState" size="small" @change="stateChange" style="width:215px">
                         <el-option value="-1" label="全部状态" >全部状态</el-option>
                         <el-option value="-2" label="未填报">未填报</el-option>
                         <el-option value="1" label="已通过">已通过</el-option>
@@ -46,25 +46,25 @@
                         </el-select></div>
                     <div>
                         <el-tree :data="data"  @node-click="handleNodeClick" node-key="id" :default-expanded-keys="expandDate">
-                            <span class="custom-tree-node" slot-scope="{ node, data}">
-                            <span>{{ node.label }}</span>
-                            <span v-if="data.membCount != null && data.isUser == null">({{data.membCount}})</span>
-                            <div style="width:0%;float:right;">
-                            <span v-if="data.isUser == 1 && data.state == null" style="color:red;">
-                            未填报
-                            </span>
-                            <span v-if="data.isUser == 1 && data.state == 0" style="color:orange;">
-                            待审核
-                            </span>
-                            <span v-if="data.isUser == 1 && data.state == 1" style="color:green;">
-                            已通过
-                            </span>
-                            <span v-if="data.isUser == 1 && data.state == 2" style="color:red;">
-                            未通过
-                            </span>
-                            <span v-if="data.isUser == 1 && data.state == 3" style="color:red;">
-                            待提交
-                            </span>
+                            <span class="custom-tree-node" slot-scope="{ node, data}" style="width:90px">
+                                <span>{{ node.label }}</span>
+                                <span v-if="data.membCount != null && data.isUser == null">({{data.membCount}})</span>
+                                <div style="width:0%;float:right;">
+                                <span v-if="data.isUser == 1 && data.state == null" style="color:red;font-size:13px">
+                                未填报
+                                </span>
+                                <span v-if="data.isUser == 1 && data.state == 0" style="color:orange;font-size:13px">
+                                待审核({{data.workingTime.toFixed(1) + 'h'}})
+                                </span>
+                                <span v-if="data.isUser == 1 && data.state == 1" style="color:#32cd32;font-size:13px">
+                                已通过({{data.workingTime.toFixed(1) + 'h'}})
+                                </span>
+                                <span v-if="data.isUser == 1 && data.state == 2" style="color:red;font-size:13px">
+                                未通过({{data.workingTime.toFixed(1) + 'h'}})
+                                </span>
+                                <span v-if="data.isUser == 1 && data.state == 3" style="color:#409eff;font-size:13px">
+                                待提交({{data.workingTime.toFixed(1) + 'h'}})
+                                </span>
                             </div>
                         </span>
                         </el-tree>
@@ -74,15 +74,16 @@
                 <div :style="'height:'+tableHeight+'px;width:1px;background:#eee;margin-right:10px;margin-left:10px;'" ></div>
                 <div class="allDaily" style="float:left;flex-grow:1">
                     <!--系统管理员和部门负责人 -->
-                    <div class="report_title" >
-                        
-                        <span>工作日报 ({{curDate}})</span>
-                        <span v-if="permissions.reportsCompany||user.manageDeptId != 0 || permissions.reportsDept">| {{depData != null ?depData.label:""}}
-                        <span v-if="targetUid == null">
-                         - 已填写
-                        <el-link :underline="false" @click="showMembList(1)"><span style="margin-left:5px;margin-right:5px;color:green;">{{reportList.length}}</span></el-link>人,
-                        未填写<el-link :underline="false" @click="showMembList(0)"><span style="margin-left:5px;margin-right:5px;color:red;">{{(depData == null?data[0].membCount:(depData.isUser == 1?1:depData.membCount))-reportList.length | numbers}}</span></el-link>人
-                        </span>
+                    <div class="report_title" style="display:flex;justify-content: space-between;">
+                        <span>
+                            <span>工作日报 ({{curDate}})</span>
+                            <span v-if="permissions.reportsCompany||user.manageDeptId != 0 || permissions.reportsDept">| {{depData != null ?depData.label:""}}
+                            <span v-if="targetUid == null">
+                            - 已填写
+                            <el-link :underline="false" @click="showMembList(1)"><span style="margin-left:5px;margin-right:5px;color:green;">{{reportList.length}}</span></el-link>人,
+                            未填写<el-link :underline="false" @click="showMembList(0)"><span style="margin-left:5px;margin-right:5px;color:red;">{{(depData == null?data[0].membCount:(depData.isUser == 1?1:depData.membCount))-reportList.length | numbers}}</span></el-link>人
+                            </span>
+                            </span>
                         </span>
                         <span style="float:right;">
                             <el-link type="primary" style="margin-right:10px;" :underline="false" @click="isSubstitude=false;fillInReport(-1,0)">填写日报</el-link>
@@ -347,7 +348,7 @@
                         <span>{{projectList.filter(p=>p.id == domain.projectId)[0].projectDesc}}</span>
                     </el-form-item>
                     <!--如果设置了工时成本预警的预算成本项-->
-                    <el-form-item label="预算来源" v-if="timeBasecostList &&timeBasecostList.length>0">
+                    <el-form-item label="预算来源" v-if="user.company.packageProject==1&&timeBasecostList &&timeBasecostList.length>0">
                         <el-select v-model="domain.basecostId" :disabled="!canEdit" @change="$forceUpdate()">
                             <el-option v-for="item in timeBasecostList" :label="item.name" :value="item.id" :key="item.id"></el-option>
                         </el-select>
@@ -457,7 +458,7 @@
                             <span>{{projectList.filter(p=>p.id == domain.projectId)[0].projectDesc}}</span>
                         </el-form-item>
                         <!--如果设置了工时成本预警的预算成本项-->
-                        <el-form-item label="预算来源" v-if="timeBasecostList &&timeBasecostList.length>0">
+                        <el-form-item label="预算来源" v-if="user.company.packageProject==1&&timeBasecostList &&timeBasecostList.length>0">
                             <el-select v-model="domain.basecostId" :disabled="!canEdit" @change="$forceUpdate()">
                                 <el-option v-for="item in timeBasecostList" :label="item.name" :value="item.id" :key="item.id"></el-option>
                             </el-select>
@@ -3079,6 +3080,7 @@
                         //设置员工到部门下面
                         this.setUserToDept(list);
                         this.data = list;
+                        console.log('data',this.data);
                         this.expandDate = [this.data[1].id]
                         this.allData = list;
                         
@@ -3196,7 +3198,7 @@
                             list[i].children = [];
                         }
                         list[i].userList.forEach(element => {
-                            var obj = {id: element.id, label:element.name, state:element.state, parentId:element.departmentId, isUser:1};
+                            var obj = {id: element.id, label:element.name, state:element.state, parentId:element.departmentId, isUser:1,workingTime: element.workingTime};
                             list[i].children.push(obj);
                             this.membCount++;
                             cnt++;

+ 42 - 11
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue

@@ -3,12 +3,18 @@
         <!--工具条-->
         <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
             <el-form :inline="true">
-                <el-form-item label="部门" style="width: 215px">
-                    <el-cascader v-model="search.departmentIdArray" placeholder="请选择部门" style="width: 175px"
+                <el-form-item label="部门" style="width: 165px">
+                    <el-cascader v-model="search.departmentIdArray" placeholder="请选择部门" style="width: 125px"
                     :options="option" :props="{ checkStrictly: false,expandTrigger: 'hover' }" :show-all-levels="false" clearable
-                    @change="getList()" size="mini"
+                    @change="getList(1)" size="mini"
                     ></el-cascader>
+                    <!-- <el-button @click="test" size="mini">test</el-button> -->
                 </el-form-item>
+                <el-form-item label="人员" style="width: 210px">
+                        <el-select v-model="search.userIdArray" placeholder="请选择" clearable @visible-change="usersSearch" @remove-tag="usersSearch(false)" @clear="usersSearch(false)" filterable="true" size="mini" style="width: 150px" multiple collapse-tags>
+                            <el-option v-for="item in searchUsersList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                        </el-select>
+                    </el-form-item>
                 <el-form-item label="项目" style="width: 215px">
                     <el-select v-model="search.projectId" placeholder="请选择" clearable @change="getList()" filterable="true" size="mini" style="width: 175px">
                         <el-option v-for="item in projectList" :key="item.id" :label="item.projectName + item.projectCode" :value="item.id">
@@ -22,11 +28,7 @@
                     <el-button @click="batchApprove(false)"  :loading="batchDenyLoading"  :disabled="multipleSelection.length==0">批量驳回</el-button>
                 </el-form-item> -->
                 <!-- <div> -->
-                    <el-form-item label="人员" style="width: 160px">
-                        <el-select v-model="search.userId" placeholder="请选择" clearable @change="getList()" filterable="true" size="mini" style="width: 100px">
-                            <el-option v-for="item in usersList" :key="item.id" :label="item.name" :value="item.id"></el-option>
-                        </el-select>
-                    </el-form-item>
+                    
                     <el-form-item label="日期">
                         <!-- <el-date-picker v-model="search.date" :editable="false" format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
                         @change="getList()" :clearable="true" type="date" placeholder="选择工作日期"></el-date-picker> -->
@@ -305,7 +307,8 @@
                     startDate: null,
                     endDate: null,
                     state:0,
-                    userId: null
+                    userId: null,
+                    userIdArray: []
                 },
 
                 users: [],
@@ -319,6 +322,7 @@
                 logining: false,
                 multipleSelection: [],
                 usersList: [],
+                searchUsersList: [],
                 dataTime: [],
                 recordDialogVisible: false,
                 recordLists: [],
@@ -524,8 +528,19 @@
                 this.recordList();
             },
 
+            test(){
+                console.log(this.search.userId);
+            },
+            searchUserIds(deptId){
+                this.searchUsersList = this.usersList.filter(item => deptId == item.departmentId)
+            },
+            usersSearch(e){
+                if(e == false){
+                    this.getList()
+                }
+            },
             //获取待审核的数据列表
-            getList() {
+            getList(e) {
                 this.listLoading = true;
                 // let form = {}
                 // if(this.search.value==-1) {
@@ -533,11 +548,26 @@
                 // } else {
                 //     form.state = this.search.value;
                 // }
-                if (this.search.departmentIdArray == null) {
+                if(e){
+                    this.search.userIdArray = []
+                }
+                if (this.search.departmentIdArray == null || this.search.departmentIdArray.length == 0) {
                     this.search.departmentId = null;
+                    this.searchUsersList = this.usersList
                 } else {
                     var length = this.search.departmentIdArray.length;
                     this.search.departmentId = this.search.departmentIdArray[length-1];
+                    this.searchUserIds(this.search.departmentId)
+                }
+                if(this.search.userIdArray.length == 0){
+                    this.search.userId = null
+                }else{
+                    let userarr = ''
+                    for(let i in this.search.userIdArray){
+                        userarr += this.search.userIdArray[i] + ','
+                    }
+                    userarr = userarr.substring(0,userarr.length - 1)
+                    this.search.userId = userarr
                 }
                 this.http.post(this.port.report.portList, this.search,
                 res => {
@@ -683,6 +713,7 @@
                 res => {
                     if (res.code == "ok") {
                         this.usersList = res.data.records;
+                        this.searchUsersList = this.usersList
                     } else {
                         this.$message({
                         message: res.msg,

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet_h5/public/index.html

@@ -43,7 +43,7 @@
             if(urls.indexOf('mobworktime.ttkuaiban') != '-1') {
                 location.href = 'https://worktime.ttkuaiban.com';
             } else {
-                location.href = 'http://183.66.166.222:9097/';
+                location.href = 'http://mldworktime.ttkuaiban.com:9097/';
             }
         }
     </script>

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

@@ -389,7 +389,6 @@
                 projectss: [],
                 proads: [],
                 userName: '',
-                proIdx: '',
                 flgLg: true
             };
         },
@@ -459,7 +458,7 @@
                 }
             },
             fZr(item,index) {
-                var domainItem = this.form.domains[this.proIdx];
+                var domainItem = this.form.domains[this.clickIndex];
                 domainItem.projectId = item.id;
                 domainItem.projectName = item.projectName;
                 
@@ -476,14 +475,14 @@
 
                 this.showPickerUserddp = false;
                 //获取子项目
-                this.getSubprojectList(this.form.domains[this.proIdx]);
+                this.getSubprojectList(this.form.domains[this.clickIndex]);
                 
                 //加载项目相关的工程进度
                 if (this.user.company.packageEngineering == 1){
-                    this.getProjectProfessions(this.form.domains[this.proIdx],index);
+                    this.getProjectProfessions(this.form.domains[this.clickIndex],index);
                 }
                 //获取任务分组
-                this.getTaskGroups(this.form.domains[this.proIdx], this.proIdx);
+                this.getTaskGroups(this.form.domains[this.clickIndex], this.clickIndex);
 
                 // 获取1相关维度
                 this.getTaskList(domainItem.projectId)
@@ -1017,7 +1016,7 @@
                 // this.clickIndex = i;
                 // this.showPickerProject = true;
                 // console.log(i, item, this.form)
-                this.proIdx = i
+                this.clickIndex = i
                 this.showPickerUserddp = true
                 // console.log(item);
                 // this.dimension()
@@ -1031,8 +1030,9 @@
                 // });
             },
             clickPickers(i,item) {
-                console.log('wudulist',item.wuduList);
+                // console.log('wudulist',item.wuduList);
                 if (!this.canEdit) return;
+                
                 this.clickIndex = i;
                 // item.showPickDegree = true;
                 this.$set(item,'showPickDegree',true)
@@ -1552,7 +1552,7 @@
         },
         
         mounted() {
-            console.log('user',this.user)
+            // console.log('user',this.user)
             var ua = navigator.userAgent.toLowerCase();
             this.isIOSystem = this.isIOS();
             if (ua.indexOf("wxwork") > 0) {

+ 14 - 16
fhKeeper/formulahousekeeper/timesheet_h5/src/views/project/index.vue

@@ -374,20 +374,13 @@
                         forbidClick: true,
                         duration: 0
                     });
-                    let formData = new FormData();
+                    let formData = new URLSearchParams();
                     formData.append("name", this.form.projectName);
                     formData.append("code", this.form.projectCode);
                     formData.append("inchargerId", this.form.inchargerId);
                     for (var j in this.form.userId) {
                         formData.append("userId", this.form.userId[j]);
                     }
-                    
-                    let form = {
-                        name: this.form.projectName,
-                        code:this.form.projectCode,
-                        inchargerId: this.form.inchargerId,
-                        userId:this.form.userId
-                    }
                     if(this.form.id != null) {
                         // form.id = this.form.id;
                         formData.append("id", this.form.id);
@@ -398,20 +391,25 @@
                         formData.append("associateDegreeNames", null);
                     }
 
-                    if(this.form.associateDegrees.length > 0) {
+                    if(this.form.associateDegrees != null && this.form.associateDegrees.length != 0) {
                         var sss = this.form.associateDegrees.toString()
                         formData.append("associateDegrees", sss);
                     } else {
                         formData.append("associateDegrees", null);
                     }
-                    const config = {
-                        headers: {
-                        'Content-Type': 'multipart/form-data'
-                        }
-                    }    
-                    this.$axios.post("/project/editProject", formData, config)
+                    // const config = {
+                    //     // headers: {
+                    //     // 'Content-Type': 'multipart/form-data',
+                        
+                    //     // // 'Content-Type': 'application/x-www-form-urlencoded'
+                    //     // },
+                    //     // transformRequest: [function (data, headers) {
+                    //     //     return data;
+                    //     // }],
+                    // }    
+                    this.$axios.post("/project/editProject", formData)
                     .then(res => {
-                        if(res.code == "ok") {
+                        if(res.code == "ok") {    
                             this.$toast.clear();
                             this.$toast.success(this.form.id==null?'新增成功':'修改成功');
                             this.list = [];

+ 10 - 10
fhKeeper/formulahousekeeper/timesheet_h5/vue.config.js

@@ -3,18 +3,18 @@ const pxtorem = require("postcss-pxtorem");
 const path = require('path');
 const themePath = path.resolve(__dirname,'src/assets/style/theme.less');
 
-// var ip = '47.100.37.243'
+var ip = '47.101.180.183'
 // var ip = '192.168.2.30'
 // var ip = '127.0.0.1'
-var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip
-for (var i in ifaces) {
-    for (var j in ifaces[i]) {
-        var val = ifaces[i][j]
-        if (val.family === 'IPv4' && val.address !== '127.0.0.1') {
-            ip = val.address
-        }
-    }
-}
+// var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip
+// for (var i in ifaces) {
+//     for (var j in ifaces[i]) {
+//         var val = ifaces[i][j]
+//         if (val.family === 'IPv4' && val.address !== '127.0.0.1') {
+//             ip = val.address
+//         }
+//     }
+// }
 
 module.exports = {
     // 关闭eslint检查