Explorar el Código

Merge branch 'develop-v3.0.0' of http://39.106.8.246:3003/FM-dev/fm-cloud into develop-v3.0.0

lixing hace 4 años
padre
commit
ae75d17680
Se han modificado 44 ficheros con 1490 adiciones y 100 borrados
  1. 0 93
      fm-person/pom.xml
  2. 4 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/constant/CommonConstant.java
  3. 56 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/controller/WorkScheduleController.java
  4. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkPersonMapper.java
  5. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkRuleMapper.java
  6. 17 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkScheduleMapper.java
  7. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftConflictMapper.java
  8. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftDurationMapper.java
  9. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftMapper.java
  10. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftRestMapper.java
  11. 10 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkTimeMapper.java
  12. 20 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkPerson.java
  13. 25 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkRule.java
  14. 23 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkSchedule.java
  15. 25 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShift.java
  16. 18 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShiftConflict.java
  17. 22 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShiftDuration.java
  18. 20 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShiftRest.java
  19. 26 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkTime.java
  20. 18 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/dto/ScheduleListQueryDto.java
  21. 11 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/dto/WorkScheduleDto.java
  22. 101 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/service/WorkScheduleService.java
  23. 21 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/transform/WorkScheduleTransform.java
  24. 34 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/utils/StreamUtil.java
  25. 41 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/utils/Validator.java
  26. 43 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/request/DownloadFileVO.java
  27. 33 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/request/ScheduleListQueryVO.java
  28. 30 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/request/UpdateScheduleVO.java
  29. 39 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/OnePersonScheduleVO.java
  30. 21 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/ScheduleListResultVO.java
  31. 25 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/StatisticalInfoVO.java
  32. 17 0
      fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/WorkTimeResultVO.java
  33. 9 0
      fm-scheduling/src/main/resources/bootstrap.yml
  34. 168 0
      fm-scheduling/src/main/resources/db.init/table.sql
  35. 17 0
      fm-scheduling/src/main/resources/mapper/WokeScheduleMapper.xml
  36. 31 0
      fm-workflow/pom.xml
  37. 144 0
      fm-workflow/src/main/java/com/persagy/fm/workflow/controller/WorkflowController.java
  38. 229 0
      fm-workflow/src/main/java/com/persagy/fm/workflow/controller/WorkflowModelController.java
  39. 19 0
      fm-workflow/src/main/java/com/persagy/fm/workflow/handler/DeptTaskHandler.java
  40. 6 0
      fm-workflow/src/main/resources/bootstrap.yml
  41. 1 0
      fm-workflow/src/main/resources/db/init/data.sql
  42. 1 0
      fm-workflow/src/main/resources/db/init/table.sql
  43. 98 0
      fm-workflow/src/main/resources/processes/TestProcess.bpmn20.xml
  44. 7 7
      pom.xml

+ 0 - 93
fm-person/pom.xml

@@ -78,97 +78,4 @@
         </dependency>
 
     </dependencies>
-
-    <build>
-        <finalName>fm-person</finalName>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-compiler-plugin</artifactId>
-                <version>3.8.1</version>
-                <configuration>
-                    <source>1.8</source>
-                    <target>1.8</target>
-                    <encoding>UTF-8</encoding>
-                    <skip>true</skip>
-                </configuration>
-            </plugin>
-            <plugin>
-                <groupId>org.springframework.boot</groupId>
-                <artifactId>spring-boot-maven-plugin</artifactId>
-                <!-- 指定启动类 -->
-                <configuration>
-                    <mainClass>com.persagy.ServerApplication</mainClass>
-                </configuration>
-            </plugin>
-            <!-- 按照测试要求打成zip需要的配置 -->
-            <plugin>
-                <groupId>com.coderplus.maven.plugins</groupId>
-                <artifactId>copy-rename-maven-plugin</artifactId>
-                <version>1.0.1</version>
-                <executions>
-                    <execution>
-                        <id>rename-file</id>
-                        <phase>compile</phase>
-                        <goals>
-                            <goal>rename</goal>
-                        </goals>
-                        <configuration>
-                            <destinationFile>${project.build.directory}/version.txt</destinationFile>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-            <!--拷贝资源文件 copy-resources-->
-            <plugin>
-                <artifactId>maven-resources-plugin</artifactId>
-                <executions>
-                    <execution>
-                        <id>copy-resources</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>copy-resources</goal>
-                        </goals>
-                        <configuration>
-                            <resources>
-                                <resource>
-                                    <directory>src/main/resources/</directory>
-                                    <includes>
-                                        <include>**/*.yml</include>
-                                    </includes>
-                                </resource>
-                                <resource>
-                                    <directory>${project.build.directory}</directory>
-                                    <includes>
-                                        <include>**/version.txt</include>
-                                    </includes>
-                                </resource>
-                            </resources>
-                            <outputDirectory>${project.build.directory}/config</outputDirectory>
-                        </configuration>
-                    </execution>
-                    <!--拷贝启动脚本-->
-                    <execution>
-                        <id>copy-version</id>
-                        <phase>package</phase>
-                        <goals>
-                            <goal>copy-resources</goal>
-                        </goals>
-                        <configuration>
-                            <resources>
-                                <resource>
-                                    <directory>src/script</directory>
-                                </resource>
-                            </resources>
-                            <outputDirectory>${project.build.directory}</outputDirectory>
-                        </configuration>
-                    </execution>
-                </executions>
-            </plugin>
-
-
-        </plugins>
-    </build>
-
-
 </project>

+ 4 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/constant/CommonConstant.java

@@ -0,0 +1,4 @@
+package com.persagy.fm.scheduling.constant;
+
+public class CommonConstant {
+}

+ 56 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/controller/WorkScheduleController.java

@@ -0,0 +1,56 @@
+package com.persagy.fm.scheduling.controller;
+
+
+import com.persagy.fm.common.response.CommonResult;
+import com.persagy.fm.common.response.PageList;
+import com.persagy.fm.scheduling.service.WorkScheduleService;
+import com.persagy.fm.scheduling.vo.request.DownloadFileVO;
+import com.persagy.fm.scheduling.vo.request.ScheduleListQueryVO;
+import com.persagy.fm.scheduling.vo.request.UpdateScheduleVO;
+import com.persagy.fm.scheduling.vo.response.OnePersonScheduleVO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/fm/schedule/")
+public class WorkScheduleController {
+
+    private final WorkScheduleService workScheduleService;
+
+
+    @PostMapping("list")
+    public CommonResult<PageList<OnePersonScheduleVO>> scheduleList(@Validated @RequestBody ScheduleListQueryVO vo) {
+        PageList<OnePersonScheduleVO> personSchedulePage = workScheduleService.scheduleList(vo);
+        CommonResult<PageList<OnePersonScheduleVO>> success = CommonResult.SUCCESS;
+        success.setContent(personSchedulePage);
+        return success;
+    }
+
+    @PostMapping("update")
+    public CommonResult<Boolean> updateSchedule(@Validated @RequestBody UpdateScheduleVO vo) {
+        return CommonResult.success(workScheduleService.updateSchedule(vo));
+    }
+
+
+    @PostMapping("uploadFile")
+    public CommonResult<Boolean> uploadFile(HttpServletRequest file) {
+        return CommonResult.SUCCESS;
+    }
+
+
+    @PostMapping("downLoadFile")
+    public void downLoadFile(@Validated @RequestBody DownloadFileVO vo, HttpServletResponse response) {
+        vo.validData();
+    }
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkPersonMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkPerson;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkPersonMapper extends BaseMapper<WorkPerson> {
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkRuleMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkRule;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkRuleMapper extends BaseMapper<WorkRule> {
+
+}

+ 17 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkScheduleMapper.java

@@ -0,0 +1,17 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.persagy.fm.scheduling.model.WorkSchedule;
+import com.persagy.fm.scheduling.model.dto.ScheduleListQueryDto;
+import com.persagy.fm.scheduling.model.dto.WorkScheduleDto;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface WorkScheduleMapper extends BaseMapper<WorkSchedule> {
+
+
+    IPage<WorkScheduleDto> getPage(IPage<WorkSchedule> page, @Param("schedule") ScheduleListQueryDto dto );
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftConflictMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkShiftConflict;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkShiftConflictMapper extends BaseMapper<WorkShiftConflict> {
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftDurationMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkShiftDuration;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkShiftDurationMapper extends BaseMapper<WorkShiftDuration> {
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkShift;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkShiftMapper extends BaseMapper<WorkShift> {
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkShiftRestMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkShiftRest;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkShiftRestMapper extends BaseMapper<WorkShiftRest> {
+
+}

+ 10 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/dao/WorkTimeMapper.java

@@ -0,0 +1,10 @@
+package com.persagy.fm.scheduling.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.persagy.fm.scheduling.model.WorkTime;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface WorkTimeMapper extends BaseMapper<WorkTime> {
+
+}

+ 20 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkPerson.java

@@ -0,0 +1,20 @@
+package com.persagy.fm.scheduling.model;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+@Data
+@TableName(value = "work_person")
+public class WorkPerson extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "person_id")
+    private String personId;
+    @TableField(value = "project_id")
+    private String projectId;
+    @TableField(value = "dept_id")
+    private String deptId;
+
+}

+ 25 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkRule.java

@@ -0,0 +1,25 @@
+package com.persagy.fm.scheduling.model;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+@Data
+@TableName(value = "work_rule")
+public class WorkRule extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "")
+    private Integer type;
+    @TableField(value = "")
+    private Integer operator;
+    @TableField(value = "")
+    private double value;
+    @TableField(value = "")
+    private String validator;
+    @TableField(value = "")
+    private Integer enable;
+
+
+}

+ 23 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkSchedule.java

@@ -0,0 +1,23 @@
+package com.persagy.fm.scheduling.model;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName(value = "work_schedule")
+public class WorkSchedule extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "work_day")
+    private Date workDay;
+    @TableField(value = "shift_id")
+    private String shiftId;
+    @TableField(value = "person_id")
+    private String personId;
+
+
+}

+ 25 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShift.java

@@ -0,0 +1,25 @@
+package com.persagy.fm.scheduling.model;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+@Data
+@TableName(value = "work_shift")
+public class WorkShift extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "shift_abbr")
+    private String shiftAbbr;
+    @TableField(value = "shift_name")
+    private String shiftName;
+    @TableField(value = "start_time")
+    private String startTime;
+    @TableField(value = "end_time")
+    private String endTime;
+    @TableField(value = "work_duration")
+    private long workDuration;
+
+
+}

+ 18 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShiftConflict.java

@@ -0,0 +1,18 @@
+package com.persagy.fm.scheduling.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+@Data
+@TableName(value = "work_shift_conflict")
+public class WorkShiftConflict extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "source_shift_id")
+    private String sourceShiftId;
+    @TableField(value = "current_shift_id")
+    private String currentShiftId;
+
+
+}

+ 22 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShiftDuration.java

@@ -0,0 +1,22 @@
+package com.persagy.fm.scheduling.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName(value = "work_shift_duration")
+public class WorkShiftDuration extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "shift_id")
+    private String shiftId;
+    @TableField(value = "start_time")
+    private Date startTime;
+    @TableField(value = "end_time")
+    private Date endTime;
+
+
+}

+ 20 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkShiftRest.java

@@ -0,0 +1,20 @@
+package com.persagy.fm.scheduling.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+@Data
+@TableName(value = "work_shift_rest")
+public class WorkShiftRest extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "shift_id")
+    private String shiftId;
+    @TableField(value = "start_time")
+    private String startTime;
+    @TableField(value = "end_time")
+    private String endTime;
+
+
+}

+ 26 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/WorkTime.java

@@ -0,0 +1,26 @@
+package com.persagy.fm.scheduling.model;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.persagy.fm.common.model.entity.AuditableEntity;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+@TableName(value = "work_time")
+public class WorkTime extends AuditableEntity<WorkSchedule> {
+
+    @TableField(value = "shift_id")
+    private String shiftId;
+    @TableField(value = "person_id")
+    private String personId;
+    @TableField(value = "start_time")
+    private Date startTime;
+    @TableField(value = "end_time")
+    private Date endTime;
+    @TableField(value = "work_times")
+    private long workTimes;
+
+
+}

+ 18 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/dto/ScheduleListQueryDto.java

@@ -0,0 +1,18 @@
+package com.persagy.fm.scheduling.model.dto;
+
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.Date;
+
+@Data
+@Accessors(chain = true)
+public class ScheduleListQueryDto {
+
+
+    private Date minDate;
+    private Date maxDate;
+    private String projectId;
+    private String deptId;
+}

+ 11 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/model/dto/WorkScheduleDto.java

@@ -0,0 +1,11 @@
+package com.persagy.fm.scheduling.model.dto;
+
+import com.persagy.fm.scheduling.model.WorkSchedule;
+import lombok.Data;
+
+@Data
+public class WorkScheduleDto extends WorkSchedule {
+
+    private String shiftAbbr;
+
+}

+ 101 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/service/WorkScheduleService.java

@@ -0,0 +1,101 @@
+package com.persagy.fm.scheduling.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.google.common.collect.Sets;
+import com.persagy.fm.common.response.PageList;
+import com.persagy.fm.scheduling.dao.WorkScheduleMapper;
+import com.persagy.fm.scheduling.model.WorkSchedule;
+import com.persagy.fm.scheduling.model.dto.ScheduleListQueryDto;
+import com.persagy.fm.scheduling.model.dto.WorkScheduleDto;
+import com.persagy.fm.scheduling.transform.WorkScheduleTransform;
+import com.persagy.fm.scheduling.vo.request.ScheduleListQueryVO;
+import com.persagy.fm.scheduling.vo.request.UpdateScheduleVO;
+import com.persagy.fm.scheduling.vo.response.OnePersonScheduleVO;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.assertj.core.util.Lists;
+import org.springframework.stereotype.Service;
+
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class WorkScheduleService {
+
+    private final WorkScheduleMapper workScheduleMapper;
+    private final WorkScheduleTransform workScheduleTransform;
+
+
+    public PageList<OnePersonScheduleVO> scheduleList(ScheduleListQueryVO vo) {
+
+        //todo 根据父级部门id 获取下面所有子级部门的部门id
+        final Set<String> departmentIds = Sets.newHashSet();
+
+        Date minDate = new GregorianCalendar(vo.getYear(), vo.getMonth(), 1).getTime();
+        Date maxDate = new GregorianCalendar(vo.getYear(), vo.getMonth() + 1, 1).getTime();
+
+        ScheduleListQueryDto listQueryDto = new ScheduleListQueryDto().setDeptId(vo.getDeptId())
+                .setMaxDate(maxDate)
+                .setMinDate(minDate)
+                .setProjectId(vo.getProjectId());
+
+
+        Page<WorkSchedule> page = new Page<WorkSchedule>().setSize(vo.getPageSize()).setCurrent(vo.getPageNum());
+
+
+        IPage<WorkScheduleDto> workSchedulePage = workScheduleMapper.getPage(page, listQueryDto);
+        final List<WorkScheduleDto> records = workSchedulePage.getRecords();
+        if (CollectionUtils.isEmpty(records)) return new PageList<>(Lists.newArrayList(), 0);
+        ConcurrentMap<String, List<WorkScheduleDto>> personSchedulesMap = records.stream()
+                .collect(Collectors.groupingByConcurrent(WorkSchedule::getPersonId));
+
+        List<OnePersonScheduleVO> personScheduleList = personSchedulesMap
+                .entrySet()
+                .stream().map(entry -> {
+                    List<WorkScheduleDto> value = entry.getValue();
+                    List<OnePersonScheduleVO.WorkScheduleVO> workScheduleVOS = value.stream()
+                            .map(workScheduleTransform::toWorkScheduleVO)
+                            .collect(Collectors.toList());
+                    return new OnePersonScheduleVO()
+                            .setPersonInfo(new OnePersonScheduleVO.PersonInfo().setPersonId(entry.getKey()))
+                            .setWorkScheduleVOS(workScheduleVOS);
+                })
+
+                .collect(Collectors.toList());
+
+        return new PageList<>(personScheduleList, workSchedulePage.getTotal());
+    }
+
+    public Boolean updateSchedule(UpdateScheduleVO vo) {
+        List<UpdateScheduleVO.ScheduleVO> scheduleVOS = vo.getScheduleVOS();
+        if (CollectionUtils.isEmpty(scheduleVOS)) return true;
+        Set<String> ids = scheduleVOS.stream()
+                .map(UpdateScheduleVO.ScheduleVO::getId)
+                .collect(Collectors.toSet());
+        final List<Object> usefulIds = Lists.newArrayList();
+        List<WorkSchedule> workSchedules = workScheduleMapper.selectBatchIds(ids);
+        if (CollectionUtils.isEmpty(workSchedules)) throw new RuntimeException("排班数据id无效");
+        if (workSchedules.size() < ids.size()) {
+            final Set<String> dbIds = workSchedules.stream().map(WorkSchedule::getId).collect(Collectors.toSet());
+            usefulIds.addAll(dbIds);
+            String errorIdStr = ids.stream().filter(a -> !dbIds.contains(a)).collect(Collectors.joining(","));
+            log.info("错误的排班修改数据{}", errorIdStr);
+        }
+        scheduleVOS.stream().filter(usefulIds::contains).forEach(a -> workSchedules.forEach(b -> {
+            if (a.getId().equals(b.getId())) {
+                b.setShiftId(a.getShiftId());
+                b.setWorkDay(a.getWorkDay());
+            }
+        }));
+        workSchedules.forEach(workScheduleMapper::updateById);
+        return Boolean.TRUE;
+    }
+}

+ 21 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/transform/WorkScheduleTransform.java

@@ -0,0 +1,21 @@
+package com.persagy.fm.scheduling.transform;
+
+import com.persagy.fm.scheduling.model.WorkSchedule;
+import com.persagy.fm.scheduling.model.dto.WorkScheduleDto;
+import com.persagy.fm.scheduling.vo.request.UpdateScheduleVO;
+import com.persagy.fm.scheduling.vo.response.OnePersonScheduleVO;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "spring")
+public interface WorkScheduleTransform {
+
+
+
+
+    OnePersonScheduleVO.WorkScheduleVO toWorkScheduleVO(WorkScheduleDto dto);
+
+
+    WorkSchedule toWorkSchedule(UpdateScheduleVO.ScheduleVO vo);
+
+
+}

+ 34 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/utils/StreamUtil.java

@@ -0,0 +1,34 @@
+package com.persagy.fm.scheduling.utils;
+
+import java.util.Optional;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+public interface StreamUtil {
+
+
+    static <T> void actionWhen(T t, Predicate<T> condition, Consumer<T> consumer) {
+        if (condition.test(t)) consumer.accept(t);
+    }
+
+    static <T, U> void actionWhen(T t, U u, Predicate<U> condition, Consumer<T> consumer) {
+        if (condition.test(u)) consumer.accept(t);
+    }
+
+    static <T> void actionWhen(T t, boolean flag, Consumer<T> consumer) {
+        if (flag) consumer.accept(t);
+    }
+
+
+    static <T, R> Optional<R> transformWhen(T t, Predicate<T> condition, Function<T, R> function) {
+        if (condition.test(t)) return Optional.of(function.apply(t));
+        return Optional.empty();
+    }
+
+
+    static <T, R> Optional<R> transformWhen(T t, boolean flag, Function<T, R> function) {
+        if (flag) return Optional.of(function.apply(t));
+        return Optional.empty();
+    }
+}

+ 41 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/utils/Validator.java

@@ -0,0 +1,41 @@
+package com.persagy.fm.scheduling.utils;
+
+import com.google.common.collect.Lists;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+@Getter
+@RequiredArgsConstructor
+public class Validator<T> {
+
+
+    private final T t;
+    private final List<String> errors = Lists.newArrayList();
+
+
+    public static <T> Validator<T> unit(T t) {
+        return new Validator<>(Objects.requireNonNull(t));
+    }
+
+    public <U> Validator<T> mapValidate(Function<T, U> f, Predicate<U> p, String message) {
+        return validate(f.andThen(p::test)::apply, message);
+    }
+
+
+    public Validator<T> validate(Predicate<T> p, String message) {
+        if (!p.test(t)) {
+            errors.add(message);
+        }
+        return this;
+    }
+
+    public <U> Validator<U> flatMap(Function<T, U> f, Function<U, Validator<U>> validator) {
+        return f.andThen(validator).apply(t);
+    }
+
+}

+ 43 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/request/DownloadFileVO.java

@@ -0,0 +1,43 @@
+package com.persagy.fm.scheduling.vo.request;
+
+
+import com.persagy.fm.scheduling.utils.Validator;
+import lombok.Data;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+import java.util.Objects;
+
+@Data
+public class DownloadFileVO {
+
+    private Integer year;
+
+    private Integer month;
+
+    private String projectId;
+
+    private String departmentId;
+
+    private Boolean downLoadTemplate = false;
+
+
+    public void validData() {
+        if (!downLoadTemplate) {
+            Validator<DownloadFileVO> validate = Validator.unit(this)
+                    .mapValidate(DownloadFileVO::getYear, Objects::nonNull, "年份为空")
+                    .mapValidate(DownloadFileVO::getMonth, Objects::nonNull, "月份为空")
+                    .mapValidate(DownloadFileVO::getDepartmentId, StringUtils::isNotBlank, "缺少部门id")
+                    .mapValidate(DownloadFileVO::getProjectId, StringUtils::isNotBlank, "缺少项目id");
+            List<String> errors = validate.getErrors();
+            if (CollectionUtils.isNotEmpty(errors)) {
+                String errorStr = String.join(",", errors);
+                throw new RuntimeException(errorStr);
+            }
+
+        }
+    }
+
+
+}

+ 33 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/request/ScheduleListQueryVO.java

@@ -0,0 +1,33 @@
+package com.persagy.fm.scheduling.vo.request;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel("排版列表查询VO")
+public class ScheduleListQueryVO {
+
+
+    @NotNull(message = "年份为空")
+    @ApiModelProperty(value = "年份", example = "2021")
+    private Integer year;
+    @NotNull(message = "月份为空")
+    @ApiModelProperty(value = "月份", example = "1")
+    private Integer month;
+    @NotBlank(message = "项目数据为空")
+    @ApiModelProperty(value = "项目id", example = "3435654131")
+    private String projectId;
+    @NotBlank(message = "部门数据为空")
+    @ApiModelProperty(value = "部门id", example = "214457")
+    private String deptId;
+    @Min(1)
+    private Long pageSize;
+    @Min(1)
+    private Integer pageNum;
+}

+ 30 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/request/UpdateScheduleVO.java

@@ -0,0 +1,30 @@
+package com.persagy.fm.scheduling.vo.request;
+
+
+import lombok.Data;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class UpdateScheduleVO {
+
+
+    private @Valid List<ScheduleVO> scheduleVOS;
+
+
+    @Data
+    public static class ScheduleVO {
+        @NotBlank
+        private String id;
+        @NotNull
+        private String shiftId;
+        @NotNull
+        private Date workDay;
+    }
+
+
+}

+ 39 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/OnePersonScheduleVO.java

@@ -0,0 +1,39 @@
+package com.persagy.fm.scheduling.vo.response;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class OnePersonScheduleVO {
+
+    private PersonInfo personInfo;
+
+    private List<WorkScheduleVO> workScheduleVOS;
+
+    private StatisticalInfoVO statisticalInfoVO;
+
+
+    @Data
+    @Accessors(chain = true)
+    public static class PersonInfo {
+        private String personId;
+        private String majorPosition;
+        private List<String> secondaryPositions;
+
+
+    }
+
+
+    @Data
+    @Accessors(chain = true)
+    public static class WorkScheduleVO {
+        private String id;
+        private Integer day;
+        private String shiftAbbr;
+    }
+
+
+}

+ 21 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/ScheduleListResultVO.java

@@ -0,0 +1,21 @@
+package com.persagy.fm.scheduling.vo.response;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+@ApiModel(description = "排班列表的返回结果vo")
+public class ScheduleListResultVO {
+
+    @ApiModelProperty(value = "一个人员的排班内容")
+    private List<OnePersonScheduleVO> personSchedules;
+    @ApiModelProperty(value = "白班夜班的工作时间以及休息时间")
+    private WorkTimeResultVO workTimeResultVO;
+
+}

+ 25 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/StatisticalInfoVO.java

@@ -0,0 +1,25 @@
+package com.persagy.fm.scheduling.vo.response;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+
+@Data
+@Accessors(chain = true)
+public class StatisticalInfoVO {
+
+    private List<OneStatisticalInfo> statisticalInfos;
+
+    private Integer workHours;
+
+    private Integer workMinutes;
+
+    @Data
+    @Accessors(chain = true)
+    public static class OneStatisticalInfo {
+        private WorkTimeType workTimeType;
+        private Integer times;
+    }
+}
+

+ 17 - 0
fm-scheduling/src/main/java/com/persagy/fm/scheduling/vo/response/WorkTimeResultVO.java

@@ -0,0 +1,17 @@
+package com.persagy.fm.scheduling.vo.response;
+
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@Data
+@Accessors(chain = true)
+public class WorkTimeResultVO {
+
+    private String workTime;
+
+    private String restTime;
+
+    private WorkTimeType workTimeType;
+
+
+}

+ 9 - 0
fm-scheduling/src/main/resources/bootstrap.yml

@@ -0,0 +1,9 @@
+spring:
+  application:
+    name: fm-scheduling
+
+
+mybatis-plus:
+  type-enums-package: com.persagy.fm.scheduling.enums
+  configuration:
+    default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler

+ 168 - 0
fm-scheduling/src/main/resources/db.init/table.sql

@@ -0,0 +1,168 @@
+CREATE DATABASE IF NOT EXISTS `fm-scheduling`;
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_rule`
+(
+    `id`            VARCHAR(64) NOT NULL COMMENT '主键',
+    `type`     int(1)      NOT NULL COMMENT '规则类型 1 日排班规则 2 月排班规则 0 特殊日期',
+    `operator` int(8) COMMENT '比较符号 1大于 -1 小于 0 等于  2 大于等于  -2 小于等于',
+    `value`    double(10, 2) COMMENT '规则的限定值',
+    `validator`     varchar(64) COMMENT '规则的校验器标记',
+    `enable`        tinyint(1)  NOT NULL DEFAULT 1 COMMENT '规则是否启用',
+    `ts`            TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`       VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`      VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`         INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作规则表';
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_shift`
+(
+    `id`            VARCHAR(64)  NOT NULL COMMENT '主键',
+    `shift_abbr`    VARCHAR(64)  NOT NULL COMMENT '班次的简称',
+    `shift_name`    VARCHAR(255) NOT NULL COMMENT '班次的全称',
+    `start_time`    VARCHAR(64)  NOT NULL COMMENT '班次的开始时间',
+    `end_time`      VARCHAR(64)  NOT NULL COMMENT '班次的结束时间',
+    `work_duration` INT(8)       NOT NULL COMMENT '班次的工作时间以分钟后为单位',
+    `ts`            TIMESTAMP             default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`       VARCHAR(32)  NOT NULL COMMENT '创建人',
+    `creation_time` TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`      VARCHAR(32)  NOT NULL COMMENT '最后修改人',
+    `modified_time` TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`         INT(11)      NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作班次表';
+
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_shift_duration`
+(
+    `id`            VARCHAR(64) NOT NULL COMMENT '主键',
+    `shift_id`      VARCHAR(64) NOT NULL COMMENT '工作班次id',
+    `start_time`    date        NOT NULL COMMENT '适用时间的开始时间',
+    `end_time`      date        NOT NULL COMMENT '适用时间的结束时间',
+    `ts`            TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`       VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`      VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`         INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作班次适用日期表';
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_shift_rest`
+(
+    `id`            VARCHAR(64) NOT NULL COMMENT '主键',
+    `shift_id`      VARCHAR(64) NOT NULL COMMENT '班次的id',
+    `start_time`    VARCHAR(64) NULL COMMENT '班次的休息开始时间',
+    `end_time`      VARCHAR(64) NULL COMMENT '班次的休息结束时间',
+    `ts`            TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`       VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`      VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`         INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作班次休息时间表';
+
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_shift_conflict`
+(
+    `id`               VARCHAR(64) NOT NULL COMMENT '主键',
+    `source_shift_id`  VARCHAR(64) NOT NULL COMMENT '被比较的班次的id',
+    `current_shift_id` VARCHAR(64) NULL COMMENT '当前的班次的id',
+    `ts`            TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`          VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time`    TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`         VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time`    TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`            INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作班次冲突表';
+
+
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_person`
+(
+    `id`            VARCHAR(64) NOT NULL COMMENT '主键',
+    `person_id`     VARCHAR(64) NOT NULL COMMENT '工作人员id',
+    `dept_id`       VARCHAR ( 64) NOT NULL COMMENT '部门id',
+    `project_id`    VARCHAR(64) NOT NULL COMMENT '项目id',
+    `ts`            TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`       VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`      VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`         INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作人员档案表';
+
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_schedule`
+(
+    `id`            VARCHAR(64) NOT NULL COMMENT '主键',
+    `work_day`      datetime NOT NULL COMMENT '上班的日期',
+    `shift_id`      VARCHAR(11) NOT NULL COMMENT '班次id',
+    `person_id`      VARCHAR(64) NOT NULL COMMENT '工作人员id',
+    `ts`            TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`       VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`      VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time` TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`         INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '工作排班表';
+
+CREATE TABLE
+    IF
+    NOT EXISTS `work_time`
+(
+    `id`              VARCHAR(64) NOT NULL COMMENT '主键',
+    `shift_id`   VARCHAR(11) NOT NULL COMMENT '班次id',
+    `staff_id`   VARCHAR(64) NOT NULL COMMENT '工作人员id',
+    `start_time` datetime    NOT NULL COMMENT '工作人员的真实上班时间',
+    `end_time`   datetime    NOT NULL COMMENT '工作人员真实下班时间',
+    `work_times`  int(8)      NOT NULL COMMENT '工作人员的真实工作时长 以分钟计',
+    `ts`              TIMESTAMP            default current_timestamp on update current_timestamp NOT NULL COMMENT '乐观锁',
+    `creator`         VARCHAR(32) NOT NULL COMMENT '创建人',
+    `creation_time`   TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `modifier`        VARCHAR(32) NOT NULL COMMENT '最后修改人',
+    `modified_time`   TIMESTAMP   NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '最后修改时间',
+    `valid`           INT(11)     NOT NULL COMMENT '合法标识',
+    PRIMARY KEY (`id`)
+) ENGINE = INNODB
+  CHARSET = utf8mb4 COMMENT '员工工作时长表';
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 17 - 0
fm-scheduling/src/main/resources/mapper/WokeScheduleMapper.xml

@@ -0,0 +1,17 @@
+<?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.persagy.fm.scheduling.dao.WorkScheduleMapper">
+
+    <select id="getPage" resultType="com.persagy.fm.scheduling.model.dto.WorkScheduleDto">
+
+        select a.*,b.shift_abbr as shiftAbbr
+        from work_schedule a , work_shift b, work_person c where a.shift_id = b.id
+        and a.person_id = c.id
+        and a. work_day &gt;= #{schedule.minDate}
+        and a.work_day &lt; #{schedule.maxDate}
+        and c.project_id = #{schedule.projectId}
+        and c.dept_id = #{schedule.deptId}
+
+    </select>
+</mapper>
+

+ 31 - 0
fm-workflow/pom.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>fm-cloud</artifactId>
+        <groupId>com.persagy</groupId>
+        <version>3.0.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>fm-workflow</artifactId>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!-- 配置中心(包含注册中心) -->
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>integrated-config-client</artifactId>
+            <version>${platform.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-boot-starter</artifactId>
+            <version>6.6.0</version>
+        </dependency>
+        <dependency>
+            <groupId>com.persagy</groupId>
+            <artifactId>fm-mybatis</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 144 - 0
fm-workflow/src/main/java/com/persagy/fm/workflow/controller/WorkflowController.java

@@ -0,0 +1,144 @@
+package com.persagy.fm.workflow.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.IoUtil;
+import com.persagy.fm.common.response.CommonResult;
+import com.persagy.fm.common.response.PageList;
+import com.persagy.fm.common.utils.ResultHelper;
+import org.flowable.bpmn.model.BpmnModel;
+import org.flowable.engine.*;
+import org.flowable.engine.runtime.Execution;
+import org.flowable.engine.runtime.ProcessInstance;
+import org.flowable.image.ProcessDiagramGenerator;
+import org.flowable.task.api.Task;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 工作流控制器
+ * @author Charlie Yu
+ * @date 2021-04-26
+ */
+@RestController
+@RequestMapping("workflow")
+public class WorkflowController {
+
+    @Autowired
+    private RuntimeService runtimeService;
+    @Autowired
+    private TaskService taskService;
+    @Autowired
+    private RepositoryService repositoryService;
+    @Autowired
+    private ProcessEngine processEngine;
+
+    /**
+     * 启动流程实例
+     * @return
+     */
+    @RequestMapping(value = "start")
+    public String startInstance() {
+        Map<String, Object> valueMap = new HashMap<>(16);
+        valueMap.put("userId", "xxxxxxxx");
+        valueMap.put("userName", "yyyyyyyyy");
+        valueMap.put("taskUser", "zzzzzzz");
+        valueMap.put("money", 1000);
+        ProcessInstance instance = runtimeService.startProcessInstanceByKey("Expense", valueMap);
+        return instance.getId();
+    }
+
+    /**
+     * 查询待办
+     * @param userId
+     * @return
+     */
+    @RequestMapping(value = "queryTodo")
+    public CommonResult<PageList<String>> queryTodo(@RequestBody String userId) {
+        List<Task> tasks = taskService.createTaskQuery().taskAssignee(userId).orderByTaskCreateTime().desc().list();
+        List<String> ids = CollUtil.getFieldValues(tasks, "id", String.class);
+        return ResultHelper.multi(ids);
+    }
+
+    /**
+     * 审批通过
+     * @param taskId
+     * @return
+     */
+    @RequestMapping(value = "apply")
+    public String apply(@RequestBody String taskId) {
+        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
+        if (task == null) {
+            throw new RuntimeException("流程不存在");
+        }
+        //通过审核
+        HashMap<String, Object> map = new HashMap<>(16);
+        map.put("outcome", "通过");
+        taskService.complete(taskId, map);
+        return "processed ok!";
+    }
+
+    /**
+     * 审批不通过
+     * @param taskId
+     * @return
+     */
+    @RequestMapping(value = "reject")
+    public String reject(@RequestBody String taskId) {
+        HashMap<String, Object> map = new HashMap<>(16);
+        map.put("outcome", "驳回");
+        taskService.complete(taskId, map);
+        return "reject";
+    }
+
+    /**
+     * 生成实例流程图
+     * @param httpServletResponse
+     *
+     * @param processId
+     * @throws Exception
+     */
+    @RequestMapping(value = "processDiagram")
+    public void genProcessDiagram(HttpServletResponse httpServletResponse, @RequestBody String processId) throws Exception {
+        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
+
+        //流程走完的不显示图
+        if (pi == null) {
+            return;
+        }
+        Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
+        //使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
+        String instanceId = task.getProcessInstanceId();
+        List<Execution> executions = runtimeService
+                .createExecutionQuery()
+                .processInstanceId(instanceId)
+                .list();
+
+        //得到正在执行的Activity的Id
+        List<String> activityIds = new ArrayList<>();
+        List<String> flows = new ArrayList<>();
+        for (Execution exe : executions) {
+            List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
+            activityIds.addAll(ids);
+        }
+
+        //获取流程图
+        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
+        ProcessEngineConfiguration configuration = processEngine.getProcessEngineConfiguration();
+        ProcessDiagramGenerator diagramGenerator = configuration.getProcessDiagramGenerator();
+        try(InputStream in = diagramGenerator.generateDiagram(bpmnModel,
+                "png", activityIds, flows, "宋体","宋体", "宋体",
+                configuration.getClassLoader(), 1.0, true); OutputStream out = httpServletResponse.getOutputStream()){
+            IoUtil.copy(in, out);
+        }
+    }
+}

+ 229 - 0
fm-workflow/src/main/java/com/persagy/fm/workflow/controller/WorkflowModelController.java

@@ -0,0 +1,229 @@
+package com.persagy.fm.workflow.controller;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import org.flowable.bpmn.model.Process;
+import org.flowable.bpmn.model.*;
+import org.flowable.engine.RepositoryService;
+import org.flowable.engine.repository.Deployment;
+import org.flowable.validation.ProcessValidator;
+import org.flowable.validation.ProcessValidatorFactory;
+import org.flowable.validation.ValidationError;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 工作流模型调研
+ * @author Charlie Yu
+ */
+@RestController
+@RequestMapping("workflowmodel")
+public class WorkflowModelController {
+
+    @Autowired
+    private RepositoryService repositoryService;
+
+    /**
+     * 启动流程实例
+     * @return
+     */
+    @RequestMapping(value = "create")
+    public String createModel() {
+        Deployment deploy=null;
+        try {
+            BpmnModel bpmnModel=new BpmnModel();
+            //设置流程信息
+            //此信息都可以通过前期自定义数据,使用时再查询
+            Process process = new Process();
+            process.setId("test_model_3");
+            process.setName("测试流程图三");
+            //添加流程节点信息---start
+            //创建数组存储所有流程节点信息
+            List<FlowElement> elementList=new ArrayList<>();
+            //创建开始节点
+            elementList.add(createStartFlowElement("startEvent_id_1", "开始_1"));
+            elementList.add(createEndFlowElement("endEvent_id_1", "结束_1"));
+
+            //查询普通任务节点信息
+            elementList.addAll(findUserTaskElements());
+
+            //把节点放入process
+            elementList.stream().forEach(item -> process.addFlowElement(item));
+
+            //查询各个节点的关系信息,并添加进流程
+            List<SequenceFlow> sequenceFlowList = createSequenceFlow();
+            sequenceFlowList.stream().forEach(item -> process.addFlowElement(item));
+
+            bpmnModel.addProcess(process);
+
+            //校验bpmModel
+            ProcessValidator processValidator=new ProcessValidatorFactory().createDefaultProcessValidator();
+            List<ValidationError> validationErrorList=processValidator.validate(bpmnModel);
+            if (validationErrorList.size()>0){
+                throw new RuntimeException("流程有误,请检查后重试");
+            }
+
+            String fileName="model_test_bpmn20.xml";
+
+            //生成自动布局
+///            new BpmnAutoLayout(bpmnModel).execute();
+            deploy =repositoryService.createDeployment().addBpmnModel(fileName, bpmnModel)
+//                    .tenantId("intelligentAsset")
+                    .deploy();
+        }catch (Exception e){
+
+        }finally {
+
+        }
+        return "ok";
+    }
+
+    /**
+     * 创建开始节点信息
+     * @return
+     */
+    public FlowElement createStartFlowElement(String id,String name){
+        StartEvent startEvent=new StartEvent();
+        startEvent.setId(id);
+        startEvent.setName(name);
+        return startEvent;
+    }
+
+    /**
+     * 创建结束节点信息
+     * @param id
+     * @param name
+     * @return
+     */
+    public FlowElement createEndFlowElement(String id,String name){
+        EndEvent endEvent=new EndEvent();
+        endEvent.setId(id);
+        endEvent.setName(name);
+        return endEvent;
+    }
+
+    public List<FlowElement> findUserTaskElements(){
+        List<FlowElement> taskList = new ArrayList<>();
+        taskList.add(createCommonUserTask("userTask_0", "任务0", "zhangsan"));
+        taskList.add(createCommonUserTask("userTask_1", "任务1", "zhangsan"));
+        taskList.add(createMultiUserTask("userTask_2", "任务2"));
+        return taskList;
+    }
+
+    /**
+     * 创建普通任务节点信息
+     * @param id
+     * @param name
+     * @param assignee
+     * @return
+     */
+    public FlowElement createCommonUserTask(String id,String name,String assignee){
+        UserTask userTask=new UserTask();
+        userTask.setId(id);
+        userTask.setName(name);
+        userTask.setAssignee(assignee);
+        return userTask;
+    }
+
+    /**
+     * 创建会签节点信息
+     * @param id
+     * @param name
+     * @return
+     */
+    public FlowElement createMultiUserTask(String id,String name){
+        UserTask userTask=new UserTask();
+        userTask.setId(id);
+        userTask.setName(name);
+        //分配用户
+        userTask.setAssignee("${assignee}");
+        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics=new MultiInstanceLoopCharacteristics();
+///        multiInstanceLoopCharacteristics.setCollectionString("${collectionList}");
+        //完成条件,默认所有人都完成
+        multiInstanceLoopCharacteristics.setCompletionCondition("${completionCondition}");
+        //元素变量多实例,一般和设置的assignee变量是对应的
+        multiInstanceLoopCharacteristics.setElementVariable("assignee");
+        //集合多实例,用于接收集合数据的表达式
+        multiInstanceLoopCharacteristics.setInputDataItem("${itemList}");
+        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
+        return userTask;
+
+
+    }
+
+    /**
+     * 查询各节点关联流转信息,即流转线
+     */
+    public List<SequenceFlow> createSequenceFlow() {
+        List<SequenceFlow> flowList = new ArrayList<>();
+        flowList.add(createSequenceFlow("sid_0","startEvent_id_1","userTask_0"));
+        flowList.add(createSequenceFlow("sid_1","userTask_0","userTask_1"));
+        flowList.add(createSequenceFlow("sid_2","userTask_1","userTask_2"));
+        flowList.add(createSequenceFlow("sid_3","userTask_2","endEvent_id_1"));
+        return flowList;
+    }
+
+    public SequenceFlow createSequenceFlow(String id, String sourceId, String targetId){
+        return createSequenceFlow(id, null, sourceId, targetId, null);
+    }
+
+    /**
+     * 绘制节点流转顺序
+     * @param id
+     * @param name
+     * @param targetId
+     * @param sourceId
+     * @param conditionExpression
+     * @return
+     */
+    public SequenceFlow createSequenceFlow(String id,String name,String sourceId,String targetId,String conditionExpression){
+        SequenceFlow sequenceFlow=new SequenceFlow();
+        sequenceFlow.setId(id);
+        if (ObjectUtils.isNotEmpty(name)){
+            sequenceFlow.setName(name);
+        } else {
+            sequenceFlow.setName("流转");
+        }
+        if (ObjectUtils.isNotEmpty(targetId)){
+            sequenceFlow.setTargetRef(targetId);
+        }
+        if (ObjectUtils.isNotEmpty(sourceId)){
+            sequenceFlow.setSourceRef(sourceId);
+        }
+        if (ObjectUtils.isNotEmpty(conditionExpression)){
+            sequenceFlow.setConditionExpression(conditionExpression);
+        } else {
+            sequenceFlow.setConditionExpression("${a==\"f\"}");
+        }
+        return sequenceFlow;
+    }
+
+    /**
+     * 排他网关
+     * @param id
+     * @param name
+     * @return
+     */
+    protected ExclusiveGateway createExclusiveGateway(String id, String name) {
+        ExclusiveGateway exclusiveGateway = new ExclusiveGateway();
+        exclusiveGateway.setId(id);
+        exclusiveGateway.setName(name);
+        return exclusiveGateway;
+    }
+
+    /**
+     * 并行网关
+     * @param id
+     * @param name
+     * @return
+     */
+    protected ParallelGateway createParallelGateway(String id, String name) {
+        ParallelGateway gateway = new ParallelGateway();
+        gateway.setId(id);
+        gateway.setName(name);
+        return gateway;
+    }
+}

+ 19 - 0
fm-workflow/src/main/java/com/persagy/fm/workflow/handler/DeptTaskHandler.java

@@ -0,0 +1,19 @@
+package com.persagy.fm.workflow.handler;
+
+import org.flowable.engine.delegate.TaskListener;
+import org.flowable.task.service.delegate.DelegateTask;
+
+/**
+ * 指定部门的部门负责人
+ */
+public class DeptTaskHandler implements TaskListener {
+
+    @Override
+    public void notify(DelegateTask delegateTask) {
+        String deptId = delegateTask.getAssignee();
+        // TODO 查询指定部门负责人
+        String personId = deptId;
+        delegateTask.setAssignee(personId);
+        delegateTask.addCandidateUser(personId);
+    }
+}

+ 6 - 0
fm-workflow/src/main/resources/bootstrap.yml

@@ -0,0 +1,6 @@
+spring:
+  application:
+    name: fm-workflow
+
+
+

+ 1 - 0
fm-workflow/src/main/resources/db/init/data.sql

@@ -0,0 +1 @@
+select 1 from dual;

+ 1 - 0
fm-workflow/src/main/resources/db/init/table.sql

@@ -0,0 +1 @@
+select 1 from dual;

+ 98 - 0
fm-workflow/src/main/resources/processes/TestProcess.bpmn20.xml

@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
+             typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
+             targetNamespace="http://www.flowable.org/processdef">
+    <process id="Expense" name="ExpenseProcess" isExecutable="true">
+        <documentation>报销流程</documentation>
+        <startEvent id="start" name="开始"></startEvent>
+        <userTask id="fillTask" name="出差报销" flowable:assignee="${taskUser}">
+            <extensionElements>
+                <modeler:initiator-can-complete xmlns:modeler="http://flowable.org/modeler">
+                    <![CDATA[false]]></modeler:initiator-can-complete>
+            </extensionElements>
+        </userTask>
+        <exclusiveGateway id="judgeTask"></exclusiveGateway>
+        <userTask id="directorTak" name="经理审批" flowable:assignee="经理"/>
+        <userTask id="bossTask" name="老板审批" flowable:assignee="老板"/>
+        <endEvent id="end" name="结束"></endEvent>
+        <sequenceFlow id="directorNotPassFlow" name="驳回" sourceRef="directorTak" targetRef="fillTask">
+            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
+        </sequenceFlow>
+        <sequenceFlow id="bossNotPassFlow" name="驳回" sourceRef="bossTask" targetRef="fillTask">
+            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='驳回'}]]></conditionExpression>
+        </sequenceFlow>
+        <sequenceFlow id="flow1" sourceRef="start" targetRef="fillTask"></sequenceFlow>
+        <sequenceFlow id="flow2" sourceRef="fillTask" targetRef="judgeTask"></sequenceFlow>
+        <sequenceFlow id="judgeMore" name="大于500元" sourceRef="judgeTask" targetRef="bossTask">
+            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money > 500}]]></conditionExpression>
+        </sequenceFlow>
+        <sequenceFlow id="bossPassFlow" name="通过" sourceRef="bossTask" targetRef="end">
+            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
+        </sequenceFlow>
+        <sequenceFlow id="directorPassFlow" name="通过" sourceRef="directorTak" targetRef="end">
+            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${outcome=='通过'}]]></conditionExpression>
+        </sequenceFlow>
+        <sequenceFlow id="judgeLess" name="小于500元" sourceRef="judgeTask" targetRef="directorTak">
+            <conditionExpression xsi:type="tFormalExpression"><![CDATA[${money <= 500}]]></conditionExpression>
+        </sequenceFlow>
+    </process>
+    <bpmndi:BPMNDiagram id="BPMNDiagram_Expense">
+        <bpmndi:BPMNPlane bpmnElement="Expense" id="BPMNPlane_Expense">
+            <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start">
+                <omgdc:Bounds height="30.0" width="30.0" x="285.0" y="135.0"></omgdc:Bounds>
+            </bpmndi:BPMNShape>
+            <bpmndi:BPMNShape bpmnElement="fillTask" id="BPMNShape_fillTask">
+                <omgdc:Bounds height="80.0" width="100.0" x="405.0" y="110.0"></omgdc:Bounds>
+            </bpmndi:BPMNShape>
+            <bpmndi:BPMNShape bpmnElement="judgeTask" id="BPMNShape_judgeTask">
+                <omgdc:Bounds height="40.0" width="40.0" x="585.0" y="130.0"></omgdc:Bounds>
+            </bpmndi:BPMNShape>
+            <bpmndi:BPMNShape bpmnElement="directorTak" id="BPMNShape_directorTak">
+                <omgdc:Bounds height="80.0" width="100.0" x="735.0" y="110.0"></omgdc:Bounds>
+            </bpmndi:BPMNShape>
+            <bpmndi:BPMNShape bpmnElement="bossTask" id="BPMNShape_bossTask">
+                <omgdc:Bounds height="80.0" width="100.0" x="555.0" y="255.0"></omgdc:Bounds>
+            </bpmndi:BPMNShape>
+            <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end">
+                <omgdc:Bounds height="28.0" width="28.0" x="771.0" y="281.0"></omgdc:Bounds>
+            </bpmndi:BPMNShape>
+            <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
+                <omgdi:waypoint x="315.0" y="150.0"></omgdi:waypoint>
+                <omgdi:waypoint x="405.0" y="150.0"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
+                <omgdi:waypoint x="505.0" y="150.16611295681062"></omgdi:waypoint>
+                <omgdi:waypoint x="585.4333333333333" y="150.43333333333334"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="judgeLess" id="BPMNEdge_judgeLess">
+                <omgdi:waypoint x="624.5530726256983" y="150.44692737430168"></omgdi:waypoint>
+                <omgdi:waypoint x="735.0" y="150.1392757660167"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="directorNotPassFlow" id="BPMNEdge_directorNotPassFlow">
+                <omgdi:waypoint x="785.0" y="110.0"></omgdi:waypoint>
+                <omgdi:waypoint x="785.0" y="37.0"></omgdi:waypoint>
+                <omgdi:waypoint x="455.0" y="37.0"></omgdi:waypoint>
+                <omgdi:waypoint x="455.0" y="110.0"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="bossPassFlow" id="BPMNEdge_bossPassFlow">
+                <omgdi:waypoint x="655.0" y="295.0"></omgdi:waypoint>
+                <omgdi:waypoint x="771.0" y="295.0"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="judgeMore" id="BPMNEdge_judgeMore">
+                <omgdi:waypoint x="605.4340277777778" y="169.56597222222223"></omgdi:waypoint>
+                <omgdi:waypoint x="605.1384083044983" y="255.0"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="directorPassFlow" id="BPMNEdge_directorPassFlow">
+                <omgdi:waypoint x="785.0" y="190.0"></omgdi:waypoint>
+                <omgdi:waypoint x="785.0" y="281.0"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+            <bpmndi:BPMNEdge bpmnElement="bossNotPassFlow" id="BPMNEdge_bossNotPassFlow">
+                <omgdi:waypoint x="555.0" y="295.0"></omgdi:waypoint>
+                <omgdi:waypoint x="455.0" y="295.0"></omgdi:waypoint>
+                <omgdi:waypoint x="455.0" y="190.0"></omgdi:waypoint>
+            </bpmndi:BPMNEdge>
+        </bpmndi:BPMNPlane>
+    </bpmndi:BPMNDiagram>
+</definitions>

+ 7 - 7
pom.xml

@@ -7,7 +7,7 @@
 
     <parent>
         <groupId>com.persagy</groupId>
-        <artifactId>fm-parent</artifactId>
+        <artifactId>fm-package</artifactId>
         <version>3.0.0</version>
     </parent>
 
@@ -21,6 +21,7 @@
         <module>fm-qrcode</module>
         <module>fm-scheduling</module>
         <module>fm-supplier</module>
+        <module>fm-workflow</module>
     </modules>
 
     <repositories>
@@ -37,6 +38,11 @@
     </repositories>
 
     <dependencies>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
         <!-- 项目启动 -->
         <dependency>
             <groupId>com.persagy</groupId>
@@ -47,11 +53,5 @@
             <groupId>com.persagy</groupId>
             <artifactId>fm-common</artifactId>
         </dependency>
-<!--        &lt;!&ndash; fm 翻译器 &ndash;&gt;-->
-<!--        <dependency>-->
-<!--            <groupId>com.persagy</groupId>-->
-<!--            <artifactId>fm-translate</artifactId>-->
-<!--            <version>3.0.0</version>-->
-<!--        </dependency>-->
     </dependencies>
 </project>