Bladeren bron

出差关联接口等等

seyason 3 jaren geleden
bovenliggende
commit
f43b759c77
25 gewijzigde bestanden met toevoegingen van 568 en 85 verwijderingen
  1. 114 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/BustripProjectController.java
  2. 5 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  3. 9 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  4. 12 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserYearleaveSettingController.java
  5. 7 16
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/BusinessTrip.java
  6. 82 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/BustripProject.java
  7. 13 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  8. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/BustripProjectMapper.java
  9. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/BustripProjectService.java
  10. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/DingDingService.java
  11. 22 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/BusinessTripServiceImpl.java
  12. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/BustripProjectServiceImpl.java
  13. 20 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java
  14. 30 29
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  15. 14 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  16. 29 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  17. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml
  18. 3 5
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/BusinessTripMapper.xml
  19. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/BustripProjectMapper.xml
  20. 3 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml
  21. 34 0
      fhKeeper/formulahousekeeper/octopus/src/views/customer/list.vue
  22. 20 0
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/config/RestTemplateConfig.java
  23. 63 12
      fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CompanyController.java
  24. 6 5
      fhKeeper/formulahousekeeper/ops-platform/src/main/resources/application.yml
  25. 6 2
      fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue

+ 114 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/BustripProjectController.java

@@ -0,0 +1,114 @@
+package com.management.platform.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.BusinessTrip;
+import com.management.platform.entity.BustripProject;
+import com.management.platform.entity.Project;
+import com.management.platform.entity.ReportExtraDegree;
+import com.management.platform.mapper.BusinessTripMapper;
+import com.management.platform.mapper.ProjectMapper;
+import com.management.platform.mapper.ReportExtraDegreeMapper;
+import com.management.platform.service.BusinessTripService;
+import com.management.platform.service.BustripProjectService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2022-05-17
+ */
+@RestController
+@RequestMapping("/bustrip-project")
+public class BustripProjectController {
+    @Resource
+    BustripProjectService bustripProjectService;
+    @Resource
+    ReportExtraDegreeMapper reportExtraDegreeMapper;
+    @Resource
+    ProjectMapper projectMapper;
+    @Resource
+    BusinessTripMapper businessTripMapper;
+
+    @RequestMapping("/addOrMod")
+    public HttpRespMsg addOrMod(BustripProject item) {
+        HttpRespMsg msg = new HttpRespMsg();
+        BusinessTrip businessTrip = businessTripMapper.selectById(item.getBustripId());
+        //检查时间段不能超出总时间段的显示
+        if (item.getStartDate() == null || item.getEndDate() == null) {
+            msg.setError("开始日期和结束日期不能为空");
+        } else {
+            DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            //校验比对日期
+            if (item.getStartDate().isBefore(businessTrip.getStartDate())) {
+                msg.setError("开始日期不得早于"+dtf.format(businessTrip.getStartDate()));
+            } else if (item.getEndDate().isAfter(businessTrip.getEndDate())) {
+                msg.setError("结束日期不得晚于"+dtf.format(businessTrip.getEndDate()));
+            } else {
+                if (item.getDegreeId() != null) {
+                    String name = reportExtraDegreeMapper.selectById(item.getDegreeId()).getName();
+                    item.setDegreeName(name);
+                }
+                bustripProjectService.saveOrUpdate(item);
+                //检查出差的关联标记
+                if (businessTrip.getIsLinked() == 0) {
+                    businessTrip.setIsLinked(1);
+                    businessTripMapper.updateById(businessTrip);
+                }
+            }
+        }
+
+        return msg;
+    }
+
+    @RequestMapping("/delete")
+    public HttpRespMsg delete(Integer id) {
+        BustripProject bp = bustripProjectService.getById(id);
+        bustripProjectService.removeById(id);
+        int leftCount = bustripProjectService.count(new QueryWrapper<BustripProject>().eq("bustrip_id", bp.getBustripId()));
+        //检查出差的关联标记
+        if (leftCount == 0) {
+            BusinessTrip businessTrip = new BusinessTrip();
+            businessTrip.setId(bp.getBustripId());
+            businessTrip.setIsLinked(0);//没有关联的项目了
+            businessTripMapper.updateById(businessTrip);
+        }
+        return new HttpRespMsg();
+    }
+
+    @RequestMapping("/list")
+    public HttpRespMsg list(Integer bustripId) {
+        List<BustripProject> list = bustripProjectService.list(new QueryWrapper<BustripProject>().eq("bustrip_id", bustripId));
+        List<Integer> collect = list.stream().map(BustripProject::getProjectId).collect(Collectors.toList());
+        if (collect.size() > 0) {
+            List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", collect));
+            list.forEach(li->{
+                if (li.getProjectId() != null) {
+                    Optional<Project> first = projectList.stream().filter(p -> p.getId().equals(li.getProjectId())).findFirst();
+                    if (first.isPresent()) {
+                        li.setProjectName(first.get().getProjectName());
+                    }
+                }
+            });
+        }
+
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.data = list;
+        return msg;
+    }
+
+}
+

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

@@ -384,6 +384,11 @@ public class ProjectController {
     @RequestMapping("/getDegreeList")
     public HttpRespMsg getDegreeList(Integer projectId) {
         Project project = projectService.getById(projectId);
+        if (project == null) {
+            HttpRespMsg msg = new HttpRespMsg();
+            msg.setError("该项目已不存在");
+            return msg;
+        }
         String associateDegrees = project.getAssociateDegrees();
         String names = project.getAssociateDegreeNames();
         HttpRespMsg msg = new HttpRespMsg();

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

@@ -730,6 +730,14 @@ public class ReportController {
                 httpRespMsg.setError("请填写工作时长");
                 return httpRespMsg;
             }
+            //如果锁定工作时长上限的话,需要校验
+            if (comTimeType.getLockWorktime() == 1) {
+                if (report.getWorkingTime() > comTimeType.getAllday()) {
+                    HttpRespMsg httpRespMsg = new HttpRespMsg();
+                    httpRespMsg.setError("工作时长不得超过"+comTimeType.getAllday()+"小时");
+                    return httpRespMsg;
+                }
+            }
             if (report.getIsOvertime() != null && report.getIsOvertime() == 1) {
                 if (report.getOvertimeHours() <= 0) {
                     HttpRespMsg httpRespMsg = new HttpRespMsg();
@@ -743,6 +751,7 @@ public class ReportController {
             }
         }
 
+
         //检查成本是否超过预算
         List<ProjectBasecostSetting> projectBasecostSettings = projectBasecostSettingMapper.selectList(new QueryWrapper<ProjectBasecostSetting>().eq("company_id", user.getCompanyId()).eq("alarm_type", 1));
         if (projectBasecostSettings.size() > 0) {

+ 12 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserYearleaveSettingController.java

@@ -1,10 +1,15 @@
 package com.management.platform.controller;
 
 
+import com.dingtalk.api.DefaultDingTalkClient;
+import com.dingtalk.api.DingTalkClient;
+import com.dingtalk.api.request.OapiAttendanceVacationTypeListRequest;
+import com.dingtalk.api.response.OapiAttendanceVacationTypeListResponse;
 import com.management.platform.entity.User;
 import com.management.platform.entity.UserYearleaveSetting;
 import com.management.platform.mapper.UserMapper;
 import com.management.platform.mapper.UserYearleaveSettingMapper;
+import com.management.platform.service.DingDingService;
 import com.management.platform.service.UserYearleaveSettingService;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.ListUtil;
@@ -33,6 +38,8 @@ public class UserYearleaveSettingController {
     private UserYearleaveSettingService userYearleaveSettingService;
     @Resource
     private UserYearleaveSettingMapper userYearleaveSettingMapper;
+    @Resource
+    private DingDingService dingDingService;
 
     @Resource
     private HttpServletRequest request;
@@ -75,5 +82,10 @@ public class UserYearleaveSettingController {
         msg.data = userYearleaveSettingMapper.getRichList(user.getCompanyId());
         return msg;
     }
+
+    @RequestMapping("/dingdingList")
+    public HttpRespMsg getDingDingList(Integer companyId) {
+        return dingDingService.getLeaveTypeList(companyId);
+    }
 }
 

+ 7 - 16
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/BusinessTrip.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import java.time.LocalDateTime;
 import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
+import java.util.List;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
@@ -20,7 +21,7 @@ import org.springframework.format.annotation.DateTimeFormat;
  * </p>
  *
  * @author Seyason
- * @since 2022-04-12
+ * @since 2022-05-17
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -128,26 +129,16 @@ public class BusinessTrip extends Model<BusinessTrip> {
 
     @TableField(exist = false)
     private String departmentName;
-    /**
-     * 项目id
-     */
-    @TableField("project_id")
-    private Integer projectId;
 
-    /**
-     * 自定义维度id
-     */
-    @TableField("degree_id")
-    private Integer degreeId;
+    @TableField(exist = false)
+    List<BustripProject> projectList;
 
     /**
-     * 自定义维度内容
+     * 是否已经关联
      */
-    @TableField("degree_name")
-    private String degreeName;
+    @TableField("is_linked")
+    private Integer isLinked;
 
-    @TableField(exist = false)
-    private String projectName;
 
     @Override
     protected Serializable pkVal() {

+ 82 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/BustripProject.java

@@ -0,0 +1,82 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import java.time.LocalDate;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2022-05-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class BustripProject extends Model<BustripProject> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 出差记录id
+     */
+    @TableField("bustrip_id")
+    private Integer bustripId;
+
+    /**
+     * 项目id
+     */
+    @TableField("project_id")
+    private Integer projectId;
+
+    /**
+     * 开始日期
+     */
+    @TableField("start_date")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startDate;
+
+    /**
+     * 结束日期
+     */
+    @TableField("end_date")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endDate;
+
+    /**
+     * 自定义维度id
+     */
+    @TableField("degree_id")
+    private Integer degreeId;
+
+    /**
+     * 自定义维度内容
+     */
+    @TableField("degree_name")
+    private String degreeName;
+    @TableField(exist = false)
+    private String projectName;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 13 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2022-05-08
+ * @since 2022-05-17
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -198,6 +198,18 @@ public class TimeType extends Model<TimeType> {
     @TableField("custom_text_name")
     private String customTextName;
 
+    /**
+     * 是否锁定每日填报时长
+     */
+    @TableField("lock_worktime")
+    private Integer lockWorktime;
+
+    /**
+     * 填报是否填报加班
+     */
+    @TableField("fill_overtime")
+    private Integer fillOvertime;
+
 
     @Override
     protected Serializable pkVal() {

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

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

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

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

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

@@ -31,4 +31,6 @@ public interface DingDingService {
     void syncUserWorkData(CompanyDingding dingding, String startDate, String endDate, boolean showLog);
 
     void getCorpSelfDefSmartReport(CompanyDingding dingding);
+
+    public HttpRespMsg getLeaveTypeList(Integer companyId);
 }

+ 22 - 7
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/BusinessTripServiceImpl.java

@@ -17,6 +17,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -33,6 +34,8 @@ public class BusinessTripServiceImpl extends ServiceImpl<BusinessTripMapper, Bus
     @Resource
     private BusinessTripMapper businessTripMapper;
     @Resource
+    private BustripProjectMapper bustripProjectMapper;
+    @Resource
     private TimeTypeMapper timeTypeMapper;
     @Resource
     private HttpServletRequest request;
@@ -109,12 +112,24 @@ public class BusinessTripServiceImpl extends ServiceImpl<BusinessTripMapper, Bus
         List<BusinessTrip> records = listIPager.getRecords();
         //赋值项目名称
         List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", sheet.getCompanyId()));
-        records.forEach(r->{
-            Optional<Project> first = projectList.stream().filter(p->p.getId().equals(r.getProjectId())).findFirst();
-            if (first.isPresent()) {
-                r.setProjectName(first.get().getProjectName());
-            }
-        });
+        if (records.size() > 0) {
+            List<Integer> collect = records.stream().map(BusinessTrip::getId).collect(Collectors.toList());
+            List<BustripProject> bustripProList = bustripProjectMapper.selectList(new QueryWrapper<BustripProject>().in("bustrip_id", collect));
+            records.forEach(r->{
+                List<BustripProject> filterBusProjects = bustripProList.stream().filter(bus -> bus.getBustripId().equals(r.getId())).collect(Collectors.toList());
+                filterBusProjects.forEach(bus->{
+                    if (bus.getProjectId() != null) {
+                        Optional<Project> first = projectList.stream().filter(p -> p.getId().equals(bus.getProjectId())).findFirst();
+                        if (first.isPresent()) {
+                            bus.setProjectName(first.get().getProjectName());
+                        }
+                    }
+                });
+                r.setProjectList(filterBusProjects);
+            });
+        }
+
+
 
         Long total = listIPager.getTotal();
         Map<String, Object> map = new HashMap<>();
@@ -147,7 +162,7 @@ public class BusinessTripServiceImpl extends ServiceImpl<BusinessTripMapper, Bus
     public HttpRespMsg summaryData(String keyword, String startDate, String endDate, String userId) {
         Integer companyId = userMapper.selectById(userId).getCompanyId();
         HttpRespMsg msg = new HttpRespMsg();
-        msg.data = businessTripMapper.summaryData(keyword, startDate, endDate, companyId);
+//        msg.data = businessTripMapper.summaryData(keyword, startDate, endDate, companyId);
         return msg;
     }
 }

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

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

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

@@ -224,7 +224,7 @@ public class DingDingServiceImpl implements DingDingService {
         String accessToken = null;
         if (dingding == null) {
             System.out.println("corpid不存在=="+corpid);
-            return "调用失败";
+            return "调用失败:corpid不存在==";
         }
         if (dingding.getExpireTime().isBefore(LocalDateTime.now())) {
             SysConfig config = sysConfigMapper.selectOne(new QueryWrapper<SysConfig>().eq("param_key", "dingding_suite_ticket"));
@@ -1036,6 +1036,7 @@ public class DingDingServiceImpl implements DingDingService {
                         String tagName = item.getString("tag_name");
                         if ("出差".equals(tagName)) {
                             BusinessTrip trip = new BusinessTrip();
+                            trip.setWay(4);//获取不到钉钉出差的方式,默认为其他方式
                             trip.setOwnerId(user.getId());
                             trip.setOwnerName(user.getName());
                             trip.setStartDate(LocalDateTime.parse(item.getString("begin_time"), timeDtf).toLocalDate());
@@ -1342,6 +1343,24 @@ public class DingDingServiceImpl implements DingDingService {
         }
     }
 
+    public HttpRespMsg getLeaveTypeList(Integer companyId) {
+        CompanyDingding dingding = companyDingdingMapper.selectOne(new QueryWrapper<CompanyDingding>().eq("company_id", companyId));
+
+        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", "超级管理员"));
+        req.setOpUserid(userList.get(0).getDingdingUserid());
+        req.setVacationSource("all");
+        OapiAttendanceVacationTypeListResponse rsp = null;
+        try {
+            rsp = client.execute(req, getInnerCorpToken(dingding));
+            System.out.println(rsp.getBody());
+
+        } catch (ApiException e) {
+            e.printStackTrace();
+        }
+        return new HttpRespMsg();
+    }
 
     public void activateSuite(String authCorpid, String tmpAuthCode) throws ApiException {
         DingTalkClient client= new DefaultDingTalkClient("https://oapi.dingtalk.com/service/activate_suite?suite_access_token=" + getDDSuiteAccessToken());

+ 30 - 29
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -768,6 +768,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                     } else {
                         membRowData.add("");
                         membRowData.add("");
+                        membRowData.add("");
                     }
 
                     membRowData.add((String)membMap.get("name"));
@@ -1511,35 +1512,35 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 if (startDate != null && endDate != null) {
                     btQueryWrapper.le("start_date", endDate).ge("end_date", startDate);
                 }
-                List<BusinessTrip> businessTripList = businessTripMapper.selectList(btQueryWrapper);
-                for(int i=0;i<businessTripList.size();i++){
-                    GanttDataItem curItem = new GanttDataItem();
-                    if(businessTripList.get(i).getProjectId()!=null) {
-                        Project project = projectMapper.selectById(businessTripList.get(i).getProjectId());
-                        businessTripList.get(i).setProjectName(project.getProjectName());
-                        if (!businessTripList.get(i).getProjectId().equals(btLastItemId)) {
-                            //抽取父级对象,项目名称
-                            GanttDataItem parent = new GanttDataItem();
-                            parent.id = String.valueOf(businessTripList.get(i).getProjectId());
-                            parent.userId = businessTripList.get(i).getOwnerId();
-                            parent.text = businessTripList.get(i).getProjectName();
-                            parent.render = "split";
-                            parent.start_date = businessTripList.get(i).getStartDate().toString();
-                            parent.end_date = businessTripList.get(i).getEndDate().toString();
-                            parent.time = 0;
-                            itemList.add(parent);
-                            btLastItemId = String.valueOf(businessTripList.get(i).getProjectId());
-                        }
-                        curItem.id = "出差_" + businessTripList.get(i).getId();
-                        curItem.userId = businessTripList.get(i).getOwnerId();
-                        curItem.text = "出差/" + businessTripList.get(i).getOwnerName();
-                        curItem.start_date = businessTripList.get(i).getStartDate().toString();
-                        curItem.end_date = businessTripList.get(i).getEndDate().toString();
-                        curItem.parent = btLastItemId;
-                        itemList.add(curItem);
-                        btLastItemId = String.valueOf(businessTripList.get(i).getProjectId());
-                    }
-                }
+//                List<BusinessTrip> businessTripList = businessTripMapper.selectList(btQueryWrapper);
+//                for(int i=0;i<businessTripList.size();i++){
+//                    GanttDataItem curItem = new GanttDataItem();
+//                    if(businessTripList.get(i).getProjectId()!=null) {
+//                        Project project = projectMapper.selectById(businessTripList.get(i).getProjectId());
+//                        businessTripList.get(i).setProjectName(project.getProjectName());
+//                        if (!businessTripList.get(i).getProjectId().equals(btLastItemId)) {
+//                            //抽取父级对象,项目名称
+//                            GanttDataItem parent = new GanttDataItem();
+//                            parent.id = String.valueOf(businessTripList.get(i).getProjectId());
+//                            parent.userId = businessTripList.get(i).getOwnerId();
+//                            parent.text = businessTripList.get(i).getProjectName();
+//                            parent.render = "split";
+//                            parent.start_date = businessTripList.get(i).getStartDate().toString();
+//                            parent.end_date = businessTripList.get(i).getEndDate().toString();
+//                            parent.time = 0;
+//                            itemList.add(parent);
+//                            btLastItemId = String.valueOf(businessTripList.get(i).getProjectId());
+//                        }
+//                        curItem.id = "出差_" + businessTripList.get(i).getId();
+//                        curItem.userId = businessTripList.get(i).getOwnerId();
+//                        curItem.text = "出差/" + businessTripList.get(i).getOwnerName();
+//                        curItem.start_date = businessTripList.get(i).getStartDate().toString();
+//                        curItem.end_date = businessTripList.get(i).getEndDate().toString();
+//                        curItem.parent = btLastItemId;
+//                        itemList.add(curItem);
+//                        btLastItemId = String.valueOf(businessTripList.get(i).getProjectId());
+//                    }
+//                }
             }
             String lastItemId = null;
             GanttDataItem lastParentItem = null;

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

@@ -354,9 +354,6 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                                 boolean isOldFormat = false;
                                 if (s.length < 5) {
                                     isOldFormat = true;
-                                    for (String it : s) {
-                                        System.out.println(it);
-                                    }
                                 }
 
                                 //获取到请假的开始时间和结束时间
@@ -540,7 +537,20 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 //                        } else {
 //                            ct.setWorkHours(DateTimeUtil.getHoursFromDouble(ct.getCardTime()));
 //                        }
-
+                    //仅有一次打卡,并且没有请假,外出的情况,需要补足下班的打卡时间
+                    if (ct.getStartTime().equals(ct.getEndTime()) && !"00:00".equals(ct.getStartTime())) {
+                        if (ct.getEndTime().compareTo(baseAfternoonEnd) < 0) {
+                            ct.setEndTime(baseAfternoonEnd);
+                            //重新计算时长
+                            double workTime = DateTimeUtil.getHoursFromSeconds(DateTimeUtil.getSecondsFromTime(ct.getEndTime()) - DateTimeUtil.getSecondsFromTime(ct.getStartTime()));
+                            if (ct.getStartTime().compareTo(baseMorningEnd) >= 0) {
+                                //重新计算打卡工时时,需要减去中间午休时间
+                                workTime -= restTime;
+                            }
+                            ct.setCardTime(workTime);
+                            ct.setWorkHours(DateTimeUtil.getHoursFromDouble(workTime));
+                        }
+                    }
                     UserCorpwxTime item = userCorpwxTimeMapper.selectOne(new QueryWrapper<UserCorpwxTime>().eq("corpwx_userid", curUserid)
                             .eq("create_date", localDate));
                     if (item != null) {

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

@@ -75,6 +75,7 @@ public class TimingTask {
     private WxCorpInfoMapper wxCorpInfoMapper;
     @Value(value = "${upload.path}")
     private String path;
+
     @Resource
     private ProjectMapper projectMapper;
     @Resource
@@ -83,6 +84,8 @@ public class TimingTask {
     private DingDingService dingDingService;
     @Resource
     private BusinessTripMapper businessTripMapper;
+    @Resource
+    private SysConfigMapper sysConfigMapper;
 
     //检查项目到期,距离到期时间3天内的,每天提醒
     @Scheduled(cron = "0 0 10 ? * *")
@@ -128,6 +131,7 @@ public class TimingTask {
         }
     }
 
+
     //每天2:11 同步钉钉用户前2天到未来30天时间段的打卡,请假,出差数据
     @Scheduled(cron = "0 11 2 ? * *")
     private void synDingDingWorkData() {
@@ -261,7 +265,7 @@ public class TimingTask {
         //每分钟5000条,待后续处理
         for (CompanyDingding dingding : dingdingList) {
             List<BusinessTrip> businessTrips = businessTripMapper.selectList(new QueryWrapper<BusinessTrip>().eq("company_id", dingding.getCompanyId())
-                                        .isNull("project_id"));
+                        .eq("is_linked", 0));
             //有未关联的出差数据
             if (businessTrips.size() > 0) {
                 List<String> ownerIds = businessTrips.stream().map(BusinessTrip::getOwnerId).collect(Collectors.toList());
@@ -286,6 +290,9 @@ public class TimingTask {
         }
         LocalDateTime now = LocalDateTime.now();
         LocalDate localDate = LocalDate.now();
+        if (!isPrivateDeploy) {
+            checkDingDingSuiteTicket();
+        }
 
         //判断是否是工作日,非工作日不提醒
         if (!WorkDayCalculateUtils.isWorkDay(localDate)) {
@@ -455,4 +462,25 @@ public class TimingTask {
             e.printStackTrace();
         }
     }
+
+
+    private void checkDingDingSuiteTicket() {
+        List<SysConfig> sysConfigs = sysConfigMapper.selectList(new QueryWrapper<SysConfig>().eq("param_key", "dingding_suite_ticket"));
+        if (sysConfigs.size() > 0) {
+            SysConfig sysConfig = sysConfigs.get(0);
+            LocalDateTime indate = sysConfig.getIndate();
+            //一般5个小时后过期,此处多加30s,作为缓冲
+            LocalDateTime expireTime = indate.plusHours(5).plusSeconds(30);
+            if (LocalDateTime.now().isAfter(expireTime)) {
+                //需要发出通知
+                String managerWxopenId = "o1L3L5lOrOl3_UEJjONaoT2Rne1I";
+                HashMap map = new HashMap();
+                map.put("wxOpenid", managerWxopenId);
+                map.put("name", "管理员");
+                map.put("departmentName", "管理部");
+                push(map, "钉钉的SuiteTicket没有及时下推,请快去手动更新");
+            }
+        }
+
+    }
 }

+ 1 - 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_hour_manager?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
     username: root
     password: HuoshiDB@2022
 

+ 3 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/BusinessTripMapper.xml

@@ -20,9 +20,7 @@
         <result column="status" property="status" />
         <result column="deny_reason" property="denyReason" />
         <result column="indate" property="indate" />
-        <result column="project_id" property="projectId" />
-        <result column="degree_id" property="degreeId" />
-        <result column="degree_name" property="degreeName" />
+        <result column="is_linked" property="isLinked" />
     </resultMap>
     <resultMap id="BaseResultMap1" type="com.management.platform.entity.BusinessTrip">
         <result column="owner_id" property="ownerId" />
@@ -30,11 +28,11 @@
         <result column="day_count" property="dayCount" />
         <result column="department_name" property="departmentName" />
     </resultMap>
-
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_id, owner_id, owner_name, reason, start_date, end_date, way, city_from, city_to, go_back, day_count, remark, status, deny_reason, indate, project_id, degree_id, degree_name
+        id, company_id, owner_id, owner_name, reason, start_date, end_date, way, city_from, city_to, go_back, day_count, remark, status, deny_reason, indate, is_linked
     </sql>
+
     <select id="summaryData"  resultMap="BaseResultMap1">
         select owner_id, owner_name, sum(day_count) as day_count, department.department_name as department_name from business_trip
         left join user on user.id = business_trip.owner_id

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/BustripProjectMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.BustripProjectMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.BustripProject">
+        <id column="id" property="id" />
+        <result column="bustrip_id" property="bustripId" />
+        <result column="project_id" property="projectId" />
+        <result column="start_date" property="startDate" />
+        <result column="end_date" property="endDate" />
+        <result column="degree_id" property="degreeId" />
+        <result column="degree_name" property="degreeName" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, bustrip_id, project_id, start_date, end_date, degree_id, degree_name
+    </sql>
+
+</mapper>

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

@@ -33,11 +33,13 @@
         <result column="show_corpwx_cardtime" property="showCorpwxCardtime" />
         <result column="custom_text_active" property="customTextActive" />
         <result column="custom_text_name" property="customTextName" />
+        <result column="lock_worktime" property="lockWorktime" />
+        <result column="fill_overtime" property="fillOvertime" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        company_id, allday, am, pm, month_days, hour_cost_input_type, type, pay_overtime, alert_time, multi_worktime, fix_monthcost, fill_months, custom_degree_active, custom_degree_name, alert_msg, sync_corpwx_time, need_dept_audit, report_workflow, custom_data_active, custom_data_name, finance_audit, overtime_ratio, sync_dingding, is_cro, only_importreport, show_dd_cardtime, show_corpwx_cardtime, custom_text_active, custom_text_name
+        company_id, allday, am, pm, month_days, hour_cost_input_type, type, pay_overtime, alert_time, multi_worktime, fix_monthcost, fill_months, custom_degree_active, custom_degree_name, alert_msg, sync_corpwx_time, need_dept_audit, report_workflow, custom_data_active, custom_data_name, finance_audit, overtime_ratio, sync_dingding, is_cro, only_importreport, show_dd_cardtime, show_corpwx_cardtime, custom_text_active, custom_text_name, lock_worktime, fill_overtime
     </sql>
 
 </mapper>

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

@@ -62,6 +62,7 @@
                     <el-button size="mini"  @click="editClick('B', scope.row)">修改有效期</el-button>
                     <el-button size="mini"  @click="editClick('C', scope.row)">修改版本</el-button>
                     <el-button size="mini"  @click="editClick('D', scope.row)" v-if="!scope.row.setMeal">设为已签约</el-button>
+                    <el-button size="mini"  @click="editClick('E', scope.row)" v-loading="dingdingSync" v-if="scope.row.dingdingCorpid">同步钉钉人员</el-button>
                 </template>
             </el-table-column>
         </el-table>
@@ -167,6 +168,7 @@
     export default {
         data() {
             return {
+                dingdingSync:false,
                 isMeal:0, //是否已签约
 
                 editDialogA: false,
@@ -223,9 +225,41 @@
                     this.dialogData.packageFinance = this.dialogData.packageFinance ? true : false
                 }
                 if(i == 'D'){ this.editDialogD = true }
+                if (i=='E') {
+                    //同步钉钉的组织架构人员
+                    this.startSyncDDMembs(obj);
+                }
                 console.log("data2",this.dialogData);
             },
 
+            startSyncDDMembs(row) {
+                this.dingdingSync = true;
+
+                this.http.post('/company/syncDindDingMembs', { corpid:row.dingdingCorpid},
+                res => {
+                    this.dingdingSync = false;
+                    if (res.code == "ok") {
+                        this.editDialogA = false
+                        this.$message({
+                            message: "同步完成",
+                            type: "success"
+                        })
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.dingdingSync = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+
             // 增加人数上限
             ConfirmA(){
                 this.listLoading = true;

+ 20 - 0
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/config/RestTemplateConfig.java

@@ -0,0 +1,20 @@
+package com.management.platform.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class RestTemplateConfig {
+    @Bean
+    public RestTemplate getRestTemplate() {
+        //配置HTTP超时时间
+        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory();
+        httpRequestFactory.setConnectionRequestTimeout(60000);
+        httpRequestFactory.setConnectTimeout(60000);
+        httpRequestFactory.setReadTimeout(60000);
+        return new RestTemplate(httpRequestFactory);
+    }
+}
+

+ 63 - 12
fhKeeper/formulahousekeeper/ops-platform/src/main/java/com/management/platform/controller/CompanyController.java

@@ -1,6 +1,7 @@
 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;
@@ -14,13 +15,18 @@ 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.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 java.time.LocalDateTime;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Collectors;
@@ -36,6 +42,10 @@ import java.util.stream.Collectors;
 @RestController
 @RequestMapping("/company")
 public class CompanyController {
+    @Value("${syncDDMembUrl}")
+    private String syncDDMembUrl;
+    @Autowired
+    RestTemplate restTemplate;
     @Resource
     CompanyMapper companyMapper;
     @Resource
@@ -45,6 +55,8 @@ public class CompanyController {
     @Resource
     CompanyDingdingMapper companyDingdingMapper;
 
+    public static LocalDateTime lastSyncDDTime;
+
     /**
      * 获取企业列表,显示到期时间
      * @return
@@ -64,18 +76,21 @@ public class CompanyController {
         IPage<Company> result = companyMapper.selectPage(new Page<>(pageIndex, pageSize), queryWrapper);
         List<Company> records = result.getRecords();
         List<Integer> collect = records.stream().map(Company::getId).collect(Collectors.toList());
-        List<WxCorpInfo> wxComps = wxCorpInfoMapper.selectList(new QueryWrapper<WxCorpInfo>().in("company_id", collect));
-        List<CompanyDingding> dingdingList = companyDingdingMapper.selectList(new QueryWrapper<CompanyDingding>().in("company_id", collect));
-        records.forEach(r->{
-            Optional<WxCorpInfo> first = wxComps.stream().filter(wx -> wx.getCompanyId().intValue() == r.getId()).findFirst();
-            if (first.isPresent()) {
-                r.setWxCorpid(first.get().getCorpid());
-            }
-            Optional<CompanyDingding> first1 = dingdingList.stream().filter(d -> d.getCompanyId().intValue() == r.getId()).findFirst();
-            if (first1.isPresent()) {
-                r.setDingdingCorpid(first1.get().getCorpid());
-            }
-        });
+        if (collect.size() > 0) {
+            List<WxCorpInfo> wxComps = wxCorpInfoMapper.selectList(new QueryWrapper<WxCorpInfo>().in("company_id", collect));
+            List<CompanyDingding> dingdingList = companyDingdingMapper.selectList(new QueryWrapper<CompanyDingding>().in("company_id", collect));
+            records.forEach(r->{
+                Optional<WxCorpInfo> first = wxComps.stream().filter(wx -> wx.getCompanyId().intValue() == r.getId()).findFirst();
+                if (first.isPresent()) {
+                    r.setWxCorpid(first.get().getCorpid());
+                }
+                Optional<CompanyDingding> first1 = dingdingList.stream().filter(d -> d.getCompanyId().intValue() == r.getId()).findFirst();
+                if (first1.isPresent()) {
+                    r.setDingdingCorpid(first1.get().getCorpid());
+                }
+            });
+        }
+
         msg.data = result;
         return msg;
     }
@@ -116,5 +131,41 @@ public class CompanyController {
     public HttpRespMsg setPackageList(Company company) {
         return companyService.setPackageList(company);
     }
+
+    @RequestMapping("/syncDindDingMembs")
+    public HttpRespMsg syncDingDingMembs(String corpid) {
+        LocalDateTime now = LocalDateTime.now();
+        if (lastSyncDDTime != null) {
+            if (now.isBefore(lastSyncDDTime.plusSeconds(60))) {
+                HttpRespMsg msg = new HttpRespMsg();
+                msg.setError("两次同步操作必须间隔一分钟");
+                return msg;
+            }
+        }
+        lastSyncDDTime = now;
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.APPLICATION_JSON);
+
+        ResponseEntity<HttpRespMsg> responseEntity = this.restTemplate.getForEntity(syncDDMembUrl+"?corpid="+corpid, HttpRespMsg.class);
+
+        if (responseEntity.getStatusCode() == HttpStatus.OK) {
+            HttpRespMsg msg = responseEntity.getBody();
+            if (msg.code.equals("error")) {
+                return msg;
+            } else {
+                if ("调用成功".equals(msg.data)) {
+                    return new HttpRespMsg();
+                } else {
+                    HttpRespMsg errMsg = new HttpRespMsg();
+                    errMsg.setError((String)msg.data);
+                    return errMsg;
+                }
+            }
+        } else {
+            HttpRespMsg error = new HttpRespMsg();
+            error.setError("请求失败:"+responseEntity.getStatusCode()+", "+responseEntity.getStatusCodeValue());
+            return error;
+        }
+    }
 }
 

+ 6 - 5
fhKeeper/formulahousekeeper/ops-platform/src/main/resources/application.yml

@@ -14,12 +14,12 @@ 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
-#    username: root
-#    password: HuoshiDB@2022
-    url: jdbc:mysql://47.100.37.243:7644/man_hour_manager?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
     username: root
-    password: Ziyu20141026!@@
+    password: HuoshiDB@2022
+#    url: jdbc:mysql://47.100.37.243:7644/man_hour_manager?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&rewriteBatchedStatements=true
+#    username: root
+#    password: Ziyu20141026!@@
     hikari:
       maximum-pool-size: 10
       minimum-idle: 3
@@ -106,6 +106,7 @@ referer:
     - ttkuaiban.com
     - ops.ttkuaiban.com
 excludeUrls: /wxcorp/*,/wxcorp/*/*,/dingding/*,/error,/testClient,/corpWXAuth,/wx-corp-info/*,/clean/*,/innerRoles/*
+syncDDMembUrl: http://worktime.ttkuaiban.com/api/dingding/syncCorpMembs
 
 
 

+ 6 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue

@@ -31,8 +31,12 @@
             </el-form>
         </div>
         <el-dialog title="使用说明" :visible.sync="dialogVisible" width="500px">
-            <p><a style="color:#409EFF;text-decoration:none" href="upload/工时管家使用说明.docx" download="工时管家使用说明.docx" 
-                        target="_blank">工时管家使用说明.docx</a></p>
+            <p><a style="color:#409EFF;text-decoration:none" href="upload/工时管家使用说明_基础版.docx" download="工时管家使用说明_基础版.docx" 
+                        target="_blank">工时管家使用说明_基础版.docx</a></p>
+            <p><a style="color:#409EFF;text-decoration:none" href="upload/工时管家使用说明_项目管理专业版.docx" download="工时管家使用说明_项目管理专业版.docx" 
+                        target="_blank">工时管家使用说明_项目管理专业版.docx</a></p>
+            <p><a style="color:#409EFF;text-decoration:none" href="upload/工时管家使用说明_建筑工程专业版.docx" download="工时管家使用说明_建筑工程专业版.docx" 
+                        target="_blank">工时管家使用说明_建筑工程专业版.docx</a></p>
             <!-- <p><a style="color:#409EFF;text-decoration:none" href="upload/工时管家使用说明_项目经理.docx" download="工时管家使用说明_项目经理.docx" 
                         target="_blank">工时管家使用说明_项目经理.docx</a></p>
             <p><a style="color:#409EFF;text-decoration:none" href="upload/工时管家使用说明_普通员工.docx" download="工时管家使用说明_普通员工.docx"