Browse Source

初始化

caixiaofeng 1 year ago
commit
c29db5a7cb
100 changed files with 5601 additions and 0 deletions
  1. 21 0
      .gitignore
  2. 0 0
      README.md
  3. 38 0
      flow-app/.gitignore
  4. 11 0
      flow-app/Dockerfile
  5. 22 0
      flow-app/flow-app.run.xml
  6. 96 0
      flow-app/pom.xml
  7. 13 0
      flow-app/src/main/java/com/flow/FlowApplication.java
  8. 126 0
      flow-app/src/main/resources/application-dev.yml
  9. 127 0
      flow-app/src/main/resources/application-test.yml
  10. 3 0
      flow-app/src/main/resources/application.yaml
  11. 18 0
      flow-app/src/main/resources/mapper/im/NotifyDao.xml
  12. 58 0
      flow-app/src/main/resources/mapper/workflow/FlowCcDao.xml
  13. 92 0
      flow-app/src/main/resources/mapper/workflow/FlowDefineDao.xml
  14. 77 0
      flow-app/src/main/resources/mapper/workflow/FlowInstanceDao.xml
  15. 151 0
      flow-app/src/main/resources/mapper/workflow/FlowTaskDao.xml
  16. 34 0
      flow-app/src/test/java/com/flow/FlowApplicationTest.java
  17. 47 0
      flow-common/flow-common-cache-starter/pom.xml
  18. 149 0
      flow-common/flow-common-cache-starter/src/main/java/com/flow/common/cache/configure/CacheConfiguration.java
  19. 3 0
      flow-common/flow-common-cache-starter/src/main/resources/META-INF/spring.factories
  20. 56 0
      flow-common/flow-common-core/pom.xml
  21. 143 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/configure/ConverterConfig.java
  22. 66 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/configure/JacksonSerializerConfig.java
  23. 21 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/exception/BaseException.java
  24. 102 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/exception/handler/BaseExceptionHandler.java
  25. 33 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/model/BaseMapper.java
  26. 18 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/model/CursorResult.java
  27. 28 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/model/PageResult.java
  28. 9 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/model/Query.java
  29. 205 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/model/Result.java
  30. 52 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/util/ApplicationContextUtil.java
  31. 131 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/util/RequestUtils.java
  32. 56 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/util/SortingField.java
  33. 19 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/util/StatusCode.java
  34. 20 0
      flow-common/flow-common-core/src/main/java/com/flow/common/core/util/StrUtil.java
  35. 41 0
      flow-common/flow-common-doc-starter/pom.xml
  36. 92 0
      flow-common/flow-common-doc-starter/src/main/java/com/flow/common/doc/configure/DocConfigure.java
  37. 127 0
      flow-common/flow-common-doc-starter/src/main/java/com/flow/common/doc/properties/DocProperties.java
  38. 46 0
      flow-common/flow-common-flowable-starter/pom.xml
  39. 47 0
      flow-common/flow-common-flowable-starter/src/main/java/com/flow/flowable/configure/FlowableConfigure.java
  40. 48 0
      flow-common/flow-common-mybatis-starter/pom.xml
  41. 95 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/configure/MybatisPlusConfigure.java
  42. 6 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/constant/SqlConstant.java
  43. 41 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/dao/BaseDao.java
  44. 87 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/entity/BaseEntity.java
  45. 32 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/entity/FilterCondition.java
  46. 35 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/entity/FilterGroup.java
  47. 49 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/handler/FieldMetaObjectHandler.java
  48. 51 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/service/BaseService.java
  49. 16 0
      flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/service/impl/BaseServiceImpl.java
  50. 53 0
      flow-common/flow-common-oauth2-starter/pom.xml
  51. 42 0
      flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/AuthenticationKeyGenerator.java
  52. 219 0
      flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/AuthorizationServerConfigure.java
  53. 57 0
      flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/ResourceServerConfigure.java
  54. 53 0
      flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/SecurityConfigure.java
  55. 70 0
      flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/utils/SecurityContextUtil.java
  56. 46 0
      flow-common/flow-common-redis-starter/pom.xml
  57. 43 0
      flow-common/flow-common-redis-starter/src/main/java/com/flow/common/redis/configure/LettuceRedisConfigure.java
  58. 584 0
      flow-common/flow-common-redis-starter/src/main/java/com/flow/common/redis/service/RedisService.java
  59. 41 0
      flow-common/flow-common-websocket-starter/pom.xml
  60. 126 0
      flow-common/flow-common-websocket-starter/src/main/java/com/flow/common/websocket/configure/WebSocketConfigure.java
  61. 102 0
      flow-common/flow-common-websocket-starter/src/main/java/com/flow/common/websocket/core/ClientInboundChannelInterceptor.java
  62. 26 0
      flow-common/flow-common-websocket-starter/src/main/java/com/flow/common/websocket/core/StompPrincipal.java
  63. 31 0
      flow-common/pom.xml
  64. 29 0
      flow-file/flow-file-api/pom.xml
  65. 20 0
      flow-file/flow-file-api/src/main/java/com/flow/service/FileService.java
  66. 29 0
      flow-file/flow-file-biz/pom.xml
  67. 59 0
      flow-file/flow-file-biz/src/main/java/com/flow/config/WebAppConfigurer.java
  68. 85 0
      flow-file/flow-file-biz/src/main/java/com/flow/constant/FileConstant.java
  69. 8 0
      flow-file/flow-file-biz/src/main/java/com/flow/dao/FileDao.java
  70. 110 0
      flow-file/flow-file-biz/src/main/java/com/flow/service/impl/FileServiceImpl.java
  71. 52 0
      flow-file/flow-file-biz/src/main/java/com/flow/utils/FileUtil.java
  72. 28 0
      flow-file/flow-file-controller/pom.xml
  73. 30 0
      flow-file/flow-file-controller/src/main/java/com/flow/FileController.java
  74. 21 0
      flow-file/flow-file-entity/pom.xml
  75. 31 0
      flow-file/flow-file-entity/src/main/java/com/flow/entity/StorageFile.java
  76. 30 0
      flow-file/flow-file-entity/src/main/java/com/flow/enums/CategoryEnum.java
  77. 12 0
      flow-file/flow-file-entity/src/main/java/com/flow/mapstruct/StorageFileMapper.java
  78. 11 0
      flow-file/flow-file-entity/src/main/java/com/flow/model/StorageFileQuery.java
  79. 43 0
      flow-file/pom.xml
  80. 29 0
      flow-im/flow-im-api/pom.xml
  81. 7 0
      flow-im/flow-im-api/src/main/java/com/flow/service/NotificationStrategy.java
  82. 26 0
      flow-im/flow-im-api/src/main/java/com/flow/service/NotifyService.java
  83. 28 0
      flow-im/flow-im-biz/pom.xml
  84. 12 0
      flow-im/flow-im-biz/src/main/java/com/flow/dao/NotifyDao.java
  85. 32 0
      flow-im/flow-im-biz/src/main/java/com/flow/service/impl/EmailNotificationStrategy.java
  86. 43 0
      flow-im/flow-im-biz/src/main/java/com/flow/service/impl/InAppNotificationStrategy.java
  87. 31 0
      flow-im/flow-im-biz/src/main/java/com/flow/service/impl/NotificationStrategyFactory.java
  88. 100 0
      flow-im/flow-im-biz/src/main/java/com/flow/service/impl/NotifyServiceImpl.java
  89. 28 0
      flow-im/flow-im-controller/pom.xml
  90. 20 0
      flow-im/flow-im-controller/src/main/java/com/flow/config/ImConverterConfig.java
  91. 56 0
      flow-im/flow-im-controller/src/main/java/com/flow/controller/NotifyController.java
  92. 21 0
      flow-im/flow-im-entity/pom.xml
  93. 20 0
      flow-im/flow-im-entity/src/main/java/com/flow/entity/MessageEntity.java
  94. 32 0
      flow-im/flow-im-entity/src/main/java/com/flow/entity/Notify.java
  95. 26 0
      flow-im/flow-im-entity/src/main/java/com/flow/entity/ToEmail.java
  96. 28 0
      flow-im/flow-im-entity/src/main/java/com/flow/enums/NotifyEnum.java
  97. 12 0
      flow-im/flow-im-entity/src/main/java/com/flow/mapstruct/NotifyMapper.java
  98. 17 0
      flow-im/flow-im-entity/src/main/java/com/flow/model/NotifyQuery.java
  99. 55 0
      flow-im/pom.xml
  100. 29 0
      flow-oauth/flow-oauth-api/pom.xml

+ 21 - 0
.gitignore

@@ -0,0 +1,21 @@
+# Build and Release Folders
+bin-debug/
+bin-release/
+[Oo]bj/
+[Bb]in/
+
+# Other files and folders
+.settings/
+
+# Executables
+*.swf
+*.air
+*.ipa
+*.apk
+target
+/**/*.iml
+/**/build/**
+/.idea/**
+# Project files, i.e. `.project`, `.actionScriptProperties` and `.flexProperties`
+# should NOT be excluded as they contain compiler settings and other important
+# information for Eclipse / Flash Builder.

+ 0 - 0
README.md


+ 38 - 0
flow-app/.gitignore

@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store

+ 11 - 0
flow-app/Dockerfile

@@ -0,0 +1,11 @@
+FROM openjdk:8-jdk-alpine
+MAINTAINER cxf<cxf@qq.com>
+RUN echo "Asia/Shanghai" > /etc/timezone
+RUN echo -e 'https://mirrors.aliyun.com/alpine/v3.6/main/\nhttps://mirrors.aliyun.com/alpine/v3.6/community/' > /etc/apk/repositories \
+    && apk update \
+    && apk upgrade \
+    && apk --no-cache add ttf-dejavu fontconfig
+RUN mkdir -p /app
+ADD target/flow-app-*.jar flow-app.jar
+EXPOSE 8988
+CMD java -jar flow-app.jar

+ 22 - 0
flow-app/flow-app.run.xml

@@ -0,0 +1,22 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="flow-app" type="docker-deploy" factoryName="dockerfile" server-name="天翼云">
+    <deployment type="dockerfile">
+      <settings>
+        <option name="imageTag" value="flow-app:1.0" />
+        <option name="containerName" value="flow-app" />
+        <option name="portBindings">
+          <list>
+            <DockerPortBindingImpl>
+              <option name="containerPort" value="9089" />
+              <option name="hostPort" value="9089" />
+            </DockerPortBindingImpl>
+          </list>
+        </option>
+        <option name="commandLineOptions" value="-v /home/flow/file/:/home/flow/file/&#10;--restart=always&#10;--net net-test" />
+        <option name="showCommandPreview" value="true" />
+        <option name="sourceFilePath" value="flow-app/Dockerfile" />
+      </settings>
+    </deployment>
+    <method v="2" />
+  </configuration>
+</component>

+ 96 - 0
flow-app/pom.xml

@@ -0,0 +1,96 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>lowflow</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-app</artifactId>
+    <name>app  应用模块</name>
+    <description>应用启动模块</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--oauth-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-oauth-controller</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--im-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-im-controller</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--system-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-system-controller</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--file-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-file-controller</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--workflow-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-workflow-controller</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--test-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>repackage</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+                <version>1.0.0</version>
+                <configuration>
+                    <!-- made of '[a-z0-9-_.]' -->
+                    <imageName>${project.artifactId}:${project.version}</imageName>
+                    <dockerDirectory>${project.basedir}</dockerDirectory>
+                    <resources>
+                        <resource>
+                            <targetPath>/</targetPath>
+                            <directory>${project.build.directory}</directory>
+                            <include>${project.build.finalName}.jar</include>
+                        </resource>
+                    </resources>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 13 - 0
flow-app/src/main/java/com/flow/FlowApplication.java

@@ -0,0 +1,13 @@
+package com.flow;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@MapperScan(basePackages = "com.flow.dao")
+@SpringBootApplication
+public class FlowApplication {
+    public static void main(String[] args) {
+        SpringApplication.run(FlowApplication.class, args);
+    }
+}

+ 126 - 0
flow-app/src/main/resources/application-dev.yml

@@ -0,0 +1,126 @@
+server:
+  port: 9089
+spring:
+  application:
+    name: flow-app
+  jackson:
+    serialization:
+      FAIL_ON_EMPTY_BEANS: false
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
+    url: jdbc:mysql://127.0.0.1:3306/flow?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
+    username: root
+    password: root
+    # Hikari 连接池配置
+    hikari:
+      # 最小空闲连接数量
+      minimum-idle: 5
+      # 空闲连接存活最大时间,默认600000(10分钟)
+      idle-timeout: 600000
+      # 连接池最大连接数,默认是10
+      maximum-pool-size: 10
+      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
+      auto-commit: true
+      # 连接池名称
+      pool-name: HikariPool-flow
+      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
+      max-lifetime: 1800000
+      # 数据库连接超时时间,默认30秒,即30000
+      connection-timeout: 30000
+      # 连接测试查询
+      connection-test-query: SELECT 1
+
+  redis:
+    timeout: 6000ms
+    database: 0      #Redis数据库索引(默认为0)
+    host: 127.0.0.1 #Redis服务器地址
+    port: 6379       #Redis服务器连接端口
+    lettuce:
+      shutdown-timeout: 100ms
+      pool:
+        max-active: 8
+        max-wait: -1ms
+        max-idle: 8
+        min-idle: 0
+
+  mail:
+    host: smtp.163.com
+    port: 465
+    protocol: smtps
+    default-encoding: UTF-8
+    username: 15606613391@163.com
+    password: WVTAADOTQFDLHUHM
+    test-connection: true
+    properties:
+      mail:
+        smtp:
+          auth: true
+          starttls:
+            enable: true
+            required: true
+
+flowable:
+  #关闭定时任务JOB
+  async-executor-activate: true
+  # 是否应激活异步历史执行器。
+  async-history-executor-activate: false
+  # 是否需要自动部署流程定义。
+  check-process-definitions: false
+  # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
+  database-schema-update: false
+  # 解决流程图乱码
+  activity-font-name: 宋体
+  label-font-name: 宋体
+  annotation-font-name: 宋体
+  # 历史级别
+  history-level: audit
+  # 启用历史清理
+  enable-history-cleaning: false
+  # 历史清理周期
+  history-cleaning-cycle: 0 0 1 * * ?
+  # 清理一年前数据
+  history-cleaning-after: 365d
+  # 历史清洗批次大小
+  history-cleaning-batch-size: 100
+  content:
+    enabled: false
+  app:
+    enabled: false
+  eventregistry:
+    enabled: false
+  dmn:
+    enabled: false
+  cmmn:
+    enabled: true
+  idm:
+    enabled: true
+
+  # 默认邮箱配置
+  mail:
+    server:
+      host: ${spring.mail.host}
+      username: ${spring.mail.username}
+      password: ${spring.mail.password}
+      default-from: ${spring.mail.username}
+
+
+mybatis-plus:
+  mapper-locations: classpath*:/mapper/**/*.xml
+  type-aliases-package: com.flow.entity
+  configuration:
+    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+    # 配置sql打印
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    # 开启驼峰命名映射
+    map-underscore-to-camel-case: true
+    # 开启二级缓存
+    cache-enabled: true
+    # 开启一对多;多对多懒加载
+    lazy-loading-enabled: true
+    aggressive-lazy-loading: false
+  global-config:
+    db-config:
+      id-type: AUTO
+      table-prefix:
+      #字符串查询过滤空字符串
+      where-strategy: not_empty

+ 127 - 0
flow-app/src/main/resources/application-test.yml

@@ -0,0 +1,127 @@
+server:
+  port: 9089
+spring:
+  application:
+    name: flow-app
+  jackson:
+    serialization:
+      FAIL_ON_EMPTY_BEANS: false
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver # mysql驱动包
+    url: jdbc:mysql://cxf_mysql:3306/flow?characterEncoding=utf8&useUnicode=true&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
+    username: root
+    password: '#Cai15606613391'
+    # Hikari 连接池配置
+    hikari:
+      # 最小空闲连接数量
+      minimum-idle: 5
+      # 空闲连接存活最大时间,默认600000(10分钟)
+      idle-timeout: 600000
+      # 连接池最大连接数,默认是10
+      maximum-pool-size: 10
+      # 此属性控制从池返回的连接的默认自动提交行为,默认值:true
+      auto-commit: true
+      # 连接池名称
+      pool-name: HikariPool-flow
+      # 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认1800000即30分钟
+      max-lifetime: 1800000
+      # 数据库连接超时时间,默认30秒,即30000
+      connection-timeout: 30000
+      # 连接测试查询
+      connection-test-query: SELECT 1
+
+  redis:
+    timeout: 6000ms
+    database: 0      #Redis数据库索引(默认为0)
+    host: cxf_redis #Redis服务器地址
+    port: 6379       #Redis服务器连接端口
+    password: '#Cai15606613391'
+    lettuce:
+      shutdown-timeout: 100ms
+      pool:
+        max-active: 8
+        max-wait: -1ms
+        max-idle: 8
+        min-idle: 0
+
+
+  mail:
+    host: smtp.163.com
+    port: 465
+    protocol: smtps
+    default-encoding: UTF-8
+    username: 15606613391@163.com
+    password: WVTAADOTQFDLHUHM
+    test-connection: true
+    properties:
+      mail:
+        smtp:
+          auth: true
+          starttls:
+            enable: true
+            required: true
+
+flowable:
+  #关闭定时任务JOB
+  async-executor-activate: true
+  # 是否应激活异步历史执行器。
+  async-history-executor-activate: false
+  # 是否需要自动部署流程定义。
+  check-process-definitions: false
+  # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。
+  database-schema-update: false
+  # 解决流程图乱码
+  activity-font-name: 宋体
+  label-font-name: 宋体
+  annotation-font-name: 宋体
+  # 历史级别
+  history-level: audit
+  # 启用历史清理
+  enable-history-cleaning: false
+  # 历史清理周期
+  history-cleaning-cycle: 0 0 1 * * ?
+  # 清理一年前数据
+  history-cleaning-after: 365d
+  # 历史清洗批次大小
+  history-cleaning-batch-size: 100
+  content:
+    enabled: false
+  app:
+    enabled: false
+  eventregistry:
+    enabled: false
+  dmn:
+    enabled: false
+  cmmn:
+    enabled: true
+  idm:
+    enabled: true
+
+  # 默认邮箱配置
+  mail:
+    server:
+      host: ${spring.mail.host}
+      username: ${spring.mail.username}
+      password: ${spring.mail.password}
+      default-from: ${spring.mail.username}
+
+mybatis-plus:
+  mapper-locations: classpath*:/mapper/**/*.xml
+  type-aliases-package: com.flow.entity
+  configuration:
+    default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+    # 配置sql打印
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+    # 开启驼峰命名映射
+    map-underscore-to-camel-case: true
+    # 开启二级缓存
+    cache-enabled: true
+    # 开启一对多;多对多懒加载
+    lazy-loading-enabled: true
+    aggressive-lazy-loading: false
+  global-config:
+    db-config:
+      id-type: AUTO
+      table-prefix:
+      #字符串查询过滤空字符串
+      where-strategy: not_empty

+ 3 - 0
flow-app/src/main/resources/application.yaml

@@ -0,0 +1,3 @@
+spring:
+  profiles:
+    active: dev

+ 18 - 0
flow-app/src/main/resources/mapper/im/NotifyDao.xml

@@ -0,0 +1,18 @@
+<?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.flow.dao.NotifyDao">
+
+    <select id="cursor" resultType="com.flow.entity.Notify">
+        SELECT * FROM sys_notify
+        <where>
+            receiver = #{receiver}
+            <if test="cursorId != null and cursorId != ''">
+                and id &lt; #{cursorId}
+            </if>
+            <if test="isRead != null">
+                and is_read = #{isRead}
+            </if>
+        </where>
+        order by id desc limit #{limit}
+    </select>
+</mapper>

+ 58 - 0
flow-app/src/main/resources/mapper/workflow/FlowCcDao.xml

@@ -0,0 +1,58 @@
+<?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.flow.dao.FlowCcDao">
+
+    <resultMap id="BaseResultMap" type="com.flow.entity.FlowCc">
+        <result property="id" column="id" jdbcType="INTEGER"/>
+        <result property="name" column="name" jdbcType="VARCHAR"/>
+        <result property="instanceId" column="instance_id" jdbcType="VARCHAR"/>
+        <result property="instanceName" column="instance_name" jdbcType="VARCHAR"/>
+        <result property="startUserId" column="start_user_id" jdbcType="VARCHAR"/>
+        <result property="definitionId" column="definition_id" jdbcType="VARCHAR"/>
+        <result property="startTime" column="start_time" jdbcType="TIMESTAMP"/>
+        <result property="nodeId" column="node_id" jdbcType="VARCHAR"/>
+        <result property="userId" column="user_id" jdbcType="VARCHAR"/>
+        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+
+    <select id="list" resultMap="BaseResultMap">
+        select
+            cc.id,
+            cc.name,
+            cc.instance_id,
+            HP.NAME_ as instance_name,
+            HP.START_USER_ID_ as start_user_id,
+            HP.START_TIME_ as start_time,
+            cc.definition_id,
+            cc.node_id,
+            cc.user_id,
+            cc.create_time
+        from flow_cc cc
+        inner join ACT_HI_PROCINST HP on cc.instance_id = HP.ID_
+        <where>
+            AND user_id = '${@com.flow.common.oauth2.utils.SecurityContextUtil@getUserId()}'
+            <if test="query.name != null and query.name != ''">
+                AND cc.name like CONCAT('%',#{query.name},'%')
+            </if>
+            <if test="query.instanceName != null and query.instanceName != ''">
+                AND HP.NAME_ like CONCAT('%',#{query.instanceName},'%')
+            </if>
+            <if test="query.startUserId != null and query.startUserId != ''">
+                AND HP.START_USER_ID_ = #{query.startUserId}
+            </if>
+            <if test="query.modelCode != null and query.modelCode != ''">
+                and HP.PROC_DEF_ID_ like CONCAT(#{query.modelCode},'%')
+            </if>
+            <if test="query.definitionId != null and query.definitionId != ''">
+                AND cc.definition_id = #{query.definitionId}
+            </if>
+            <if test="query.nodeId != null and query.nodeId != ''">
+                AND cc.node_id = #{query.nodeId}
+            </if>
+        </where>
+        ORDER BY
+        cc.id DESC
+    </select>
+
+</mapper>

+ 92 - 0
flow-app/src/main/resources/mapper/workflow/FlowDefineDao.xml

@@ -0,0 +1,92 @@
+<?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.flow.dao.FlowDefineDao">
+
+    <resultMap id="BaseResultMap" type="com.flow.entity.FlowDefine">
+        <!--@Table flow_process-->
+        <result property="id" column="id" jdbcType="INTEGER"/>
+        <result property="key" column="key_" jdbcType="VARCHAR"/>
+        <result property="name" column="name" jdbcType="VARCHAR"/>
+        <result property="icon" column="icon" jdbcType="VARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+        <result property="process" column="process" jdbcType="VARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+        <result property="form" column="form" jdbcType="VARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+        <result property="defineId" column="define_id" jdbcType="VARCHAR"/>
+        <result property="version" column="version" jdbcType="INTEGER"/>
+        <result property="groupId" column="group_id" jdbcType="INTEGER"/>
+        <result property="admin" column="admin" jdbcType="VARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+        <result property="settings" column="settings" jdbcType="VARCHAR" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
+        <result property="suspend" column="suspend" jdbcType="INTEGER"/>
+        <result property="deploymentId" column="deployment_id" jdbcType="VARCHAR"/>
+        <result property="description" column="description" jdbcType="VARCHAR"/>
+        <result property="createdBy" column="created_by" jdbcType="VARCHAR"/>
+        <result property="updatedBy" column="updated_by" jdbcType="VARCHAR"/>
+        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
+        <result property="updateTime" column="update_time" jdbcType="TIMESTAMP"/>
+    </resultMap>
+
+    <sql id="defProcess">
+        SELECT res.ID_               AS define_id,
+               res.CATEGORY_         AS group_id,
+               res.KEY_              AS `key_`,
+               res.NAME_             AS name,
+               res.VERSION_          AS version,
+               res.CATEGORY_         AS groupId,
+               res.SUSPENSION_STATE_ AS suspend,
+               res.DEPLOYMENT_ID_    AS deployment_id,
+               res.DESCRIPTION_      AS description,
+               pro.id,
+               pro.icon,
+               pro.process,
+               pro.form,
+               pro.admin,
+               pro.settings,
+               pro.created_by,
+               pro.updated_by,
+               pro.create_time,
+               pro.update_time
+        FROM ACT_RE_PROCDEF AS res INNER JOIN flow_define AS pro ON res.ID_ = pro.define_id
+    </sql>
+
+    <select id="getList" resultMap="BaseResultMap">
+        SELECT
+            res.ID_               AS define_id,
+            res.CATEGORY_         AS group_id,
+            res.KEY_              AS `key_`,
+            res.NAME_             AS name,
+            res.VERSION_          AS version,
+            res.CATEGORY_         AS groupId,
+            res.SUSPENSION_STATE_ AS suspend,
+            res.DEPLOYMENT_ID_    AS deployment_id,
+            res.DESCRIPTION_      AS description,
+            pro.id,
+            pro.icon,
+            pro.admin,
+            pro.created_by,
+            pro.updated_by,
+            pro.create_time,
+            pro.update_time
+        FROM ACT_RE_PROCDEF AS res INNER JOIN flow_define AS pro ON res.ID_ = pro.define_id
+        <where>
+            res.VERSION_ = (
+                SELECT max( def.VERSION_ ) FROM ACT_RE_PROCDEF as def WHERE def.KEY_ = res.KEY_
+            )
+            and res.SUSPENSION_STATE_ = 1
+            <if test="name != null and name != ''">
+                and res.NAME_ like CONCAT( '%',#{name},'%')
+            </if>
+            <if test="groupId != null and groupId != ''">
+                and res.CATEGORY_ = #{groupId}
+            </if>
+        </where>
+        order by pro.create_time desc
+    </select>
+
+    <select id="getByDefineId" resultMap="BaseResultMap">
+        <include refid="defProcess"></include>
+        <where>
+            res.ID_ = #{defineId}
+        </where>
+    </select>
+
+
+</mapper>

+ 77 - 0
flow-app/src/main/resources/mapper/workflow/FlowInstanceDao.xml

@@ -0,0 +1,77 @@
+<?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.flow.dao.FlowInstanceDao">
+
+    <resultMap id="BaseResultMap" type="com.flow.entity.FlowInstance">
+        <result property="id" column="id" jdbcType="VARCHAR"/>
+        <result property="name" column="NAME_" jdbcType="VARCHAR"/>
+        <result property="definitionId" column="PROC_DEF_ID_" jdbcType="VARCHAR"/>
+        <result property="version" column="VERSION_" jdbcType="INTEGER"/>
+        <result property="startUserId" column="START_USER_ID_" jdbcType="VARCHAR"/>
+        <result property="startTime" column="START_TIME_" jdbcType="TIMESTAMP"/>
+        <result property="endTime" column="END_TIME_" jdbcType="TIMESTAMP"/>
+        <result property="duration" column="DURATION_" jdbcType="INTEGER"/>
+        <result property="status" column="status" jdbcType="INTEGER"/>
+        <association property="currentNodes" column="id" select="getCurrentNodes"/>
+    </resultMap>
+
+    <sql id="instanceColumns">
+        SELECT
+            RE.id,
+            HP.NAME_,
+            HP.PROC_DEF_ID_,
+            HP.START_USER_ID_,
+            HP.START_TIME_,
+            HP.END_TIME_,
+            HP.DURATION_,
+            RE.status
+        FROM
+            ACT_HI_PROCINST HP
+                LEFT JOIN flow_instance RE ON HP.ID_ = RE.id
+    </sql>
+
+    <select id="list" resultMap="BaseResultMap">
+        <include refid="instanceColumns"></include>
+        <where>
+            <if test="query.startUserId != null and query.startUserId != ''">
+                AND HP.START_USER_ID_ = #{query.startUserId}
+            </if>
+            <if test="query.status!= null">
+                AND RE.status = #{query.status}
+            </if>
+            <if test="query.name!= null and query.name!= ''">
+                AND HP.NAME_ like CONCAT('%',#{query.name},'%')
+            </if>
+            <if test="query.modelCode!= null and query.modelCode!= ''">
+                AND HP.PROC_DEF_ID_ like CONCAT(#{query.modelCode},'%')
+            </if>
+            <if test="query.definitionId!= null and query.definitionId!= ''">
+                AND HP.PROC_DEF_ID_ = #{query.definitionId}
+            </if>
+            <if test="query.startDates != null and query.startDates.size()>1">
+                AND HP.START_TIME_ between #{query.startDates[0]} and #{query.startDates[1]}
+            </if>
+            <if test="query.endDates != null and query.endDates.size()>1">
+                AND HP.END_TIME_ between #{query.endDates[0]} and #{query.endDates[1]}
+            </if>
+        </where>
+        ORDER BY
+        HP.ID_ DESC
+    </select>
+    <select id="getInstance" resultMap="BaseResultMap">
+        <include refid="instanceColumns"></include>
+        WHERE
+            RE.id = #{id}
+    </select>
+
+    <select id="getCurrentNodes" resultType="java.lang.String">
+        SELECT
+            DISTINCT ACT_NAME_
+        FROM
+            ACT_RU_ACTINST
+        WHERE
+           END_TIME_ IS NULL AND PROC_INST_ID_ = #{id}
+    </select>
+
+
+</mapper>

+ 151 - 0
flow-app/src/main/resources/mapper/workflow/FlowTaskDao.xml

@@ -0,0 +1,151 @@
+<?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.flow.dao.FlowTaskDao">
+    <resultMap id="BaseResultMap" type="com.flow.entity.FlowTask">
+        <result property="id" column="id" jdbcType="VARCHAR"/>
+        <result property="name" column="name" jdbcType="VARCHAR"/>
+        <result property="assignee" column="assignee" jdbcType="VARCHAR"/>
+        <result property="startUserId" column="start_user_id" jdbcType="VARCHAR"/>
+        <result property="duration" column="duration" jdbcType="BIGINT"/>
+        <result property="instanceId" column="instance_id" jdbcType="VARCHAR"/>
+        <result property="instanceName" column="instance_name" jdbcType="VARCHAR"/>
+        <result property="definitionId" column="definition_id" jdbcType="VARCHAR"/>
+        <result property="dueDate" column="due_date" jdbcType="TIMESTAMP"/>
+        <result property="startTime" column="start_time" jdbcType="TIMESTAMP"/>
+        <result property="endTime" column="end_time" jdbcType="VARCHAR"/>
+        <result property="duration" column="duration" jdbcType="INTEGER"/>
+        <result property="status" column="status" jdbcType="INTEGER"/>
+    </resultMap>
+    <!--进行中-->
+    <sql id="runningTaskColumns">
+        SELECT
+            RP.NAME_ AS instance_name,
+            RP.START_USER_ID_ AS start_user_id,
+            RT.ID_ AS id,
+            RT.NAME_ AS NAME,
+            RT.ASSIGNEE_ AS assignee,
+            RT.PROC_INST_ID_ AS instance_id,
+            RT.PROC_DEF_ID_ AS definition_id,
+            RT.CREATE_TIME_ AS start_time,
+            NULL AS end_time,
+            RT.DUE_DATE_ AS due_date,
+            FT.STATUS
+        FROM
+            ACT_RU_TASK AS RT
+                LEFT JOIN flow_task AS FT ON FT.id = RT.ID_
+                LEFT JOIN ACT_RU_EXECUTION AS RP ON RT.PROC_INST_ID_ = RP.PROC_INST_ID_
+    </sql>
+    <select id="todo" resultMap="BaseResultMap">
+        <include refid="runningTaskColumns"></include>
+        <where>
+            RP.PARENT_ID_ IS NULL
+            <if test="query.id != null and query.id != ''">
+                and FT.id = #{query.id}
+            </if>
+            <if test="query.name != null and query.name != ''">
+                and RT.NAME_ like CONCAT('%',#{query.name},'%')
+            </if>
+            <if test="query.modelCode != null and query.modelCode != ''">
+                and RT.PROC_DEF_ID_ like CONCAT(#{query.modelCode},'%')
+            </if>
+            <if test="query.definitionId != null and query.definitionId != ''">
+                and RT.PROC_DEF_ID_ = #{query.definitionId}
+            </if>
+            <if test="query.instanceId != null and query.instanceId != ''">
+                and RT.PROC_INST_ID_ = #{query.instanceId}
+            </if>
+            <if test="query.instanceName != null and query.instanceName != ''">
+                and RP.NAME_ like CONCAT('%',#{query.instanceName},'%')
+            </if>
+            <if test="query.startUserId != null and query.startUserId != ''">
+                and RP.START_USER_ID_ = #{query.startUserId}
+            </if>
+            <if test="query.assignee != null and query.assignee != ''">
+                and RT.ASSIGNEE_ = #{query.assignee}
+            </if>
+            <if test="query.startDates != null and query.startDates.size()>1">
+                AND RT.CREATE_TIME_ between #{query.startDates[0]} and #{query.startDates[1]}
+            </if>
+        </where>
+        order by FT.id desc
+    </select>
+    <select id="running" resultMap="BaseResultMap">
+        <include refid="runningTaskColumns"></include>
+        <where>
+            RP.PARENT_ID_ IS NULL
+            <if test="id!= null and id!= ''">
+                and FT.id = #{id}
+            </if>
+        </where>
+    </select>
+    <!--已完成-->
+    <sql id="historyTaskColumns">
+        SELECT
+            FT.id,
+            HT.NAME_ AS NAME,
+            HT.ASSIGNEE_ AS assignee,
+            HT.PROC_INST_ID_ AS instance_id,
+            HT.PROC_DEF_ID_ AS definition_id,
+            HT.START_TIME_ AS start_time,
+            HT.END_TIME_ AS end_time,
+            HT.DURATION_ AS duration,
+            HT.DUE_DATE_ AS due_date,
+            HP.NAME_ as instance_name,
+            HP.START_USER_ID_ as start_user_id,
+            FT.STATUS
+        FROM
+            ACT_HI_TASKINST AS HT
+                LEFT JOIN ACT_HI_PROCINST AS HP ON HT.PROC_INST_ID_ = HP.PROC_INST_ID_
+                LEFT JOIN flow_task AS FT ON FT.id = HT.ID_
+    </sql>
+    <select id="done" resultMap="BaseResultMap">
+        <include refid="historyTaskColumns"></include>
+        <where>
+            HT.END_TIME_ is not null and FT.STATUS != 0
+            <if test="query.id != null and query.id != ''">
+                and FT.id = #{query.id}
+            </if>
+            <if test="query.name != null and query.name != ''">
+                and HP.NAME_ like CONCAT('%',#{query.name},'%')
+            </if>
+            <if test="query.status!= null">
+                AND FT.status = #{query.status}
+            </if>
+            <if test="query.modelCode != null and query.modelCode != ''">
+                and HT.PROC_DEF_ID_ like CONCAT(#{query.modelCode},'%')
+            </if>
+            <if test="query.definitionId != null and query.definitionId != ''">
+                and RT.PROC_DEF_ID_ = #{query.definitionId}
+            </if>
+            <if test="query.instanceId != null and query.instanceId != ''">
+                and HT.PROC_INST_ID_ = #{query.instanceId}
+            </if>
+            <if test="query.instanceName != null and query.instanceName != ''">
+                and HP.NAME_ like CONCAT('%',#{query.instanceName},'%')
+            </if>
+            <if test="query.startUserId != null and query.startUserId != ''">
+                and HP.START_USER_ID_ = #{query.startUserId}
+            </if>
+            <if test="query.assignee != null and query.assignee != ''">
+                and HT.ASSIGNEE_ = #{query.assignee}
+            </if>
+            <if test="query.startDates != null and query.startDates.size()>1">
+                AND HT.START_TIME_ between #{query.startDates[0]} and #{query.startDates[1]}
+            </if>
+            <if test="query.endDates != null and query.endDates.size()>1">
+                AND HT.END_TIME_ between #{query.endDates[0]} and #{query.endDates[1]}
+            </if>
+        </where>
+        order by FT.id desc
+    </select>
+    <select id="history" resultMap="BaseResultMap">
+        <include refid="historyTaskColumns"></include>
+        <where>
+            HT.END_TIME_ is not null
+            <if test="id!= null and id!= ''">
+                and FT.id = #{id}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 34 - 0
flow-app/src/test/java/com/flow/FlowApplicationTest.java

@@ -0,0 +1,34 @@
+package com.flow;
+
+
+import org.flowable.engine.HistoryService;
+import org.flowable.engine.TaskService;
+import org.flowable.task.api.Task;
+import org.flowable.task.api.history.HistoricTaskInstance;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import java.util.List;
+
+@SpringBootTest(classes = FlowApplication.class)
+@RunWith(SpringRunner.class)
+public class FlowApplicationTest {
+    @Autowired
+    private TaskService taskService;
+    @Autowired
+    private HistoryService historyService;
+    @Test
+    public void test() {
+        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
+                .processInstanceId("1774782190855913474")
+                .list();
+        System.out.println("list = " + list);
+        List<Task> list1 = taskService.createTaskQuery()
+                .processInstanceId("1774782190855913474")
+                .list();
+        System.out.println("list1 = " + list1);
+    }
+}

+ 47 - 0
flow-common/flow-common-cache-starter/pom.xml

@@ -0,0 +1,47 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-common-cache-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--jackson-->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--lombok-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--cache-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-cache</artifactId>
+        </dependency>
+        <!--redis-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-redis-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 149 - 0
flow-common/flow-common-cache-starter/src/main/java/com/flow/common/cache/configure/CacheConfiguration.java

@@ -0,0 +1,149 @@
+package com.flow.common.cache.configure;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.cache.CacheProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.interceptor.CacheErrorHandler;
+import org.springframework.cache.interceptor.KeyGenerator;
+import org.springframework.cache.interceptor.SimpleCacheErrorHandler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+import org.springframework.util.StringUtils;
+
+import java.util.Optional;
+
+@EnableConfigurationProperties(CacheProperties.class)
+@Configuration
+@EnableCaching // 开启缓存
+public class CacheConfiguration {
+    @Autowired
+    private CacheProperties cacheProperties;
+    @Value("${spring.application.name}")
+    private String name;
+
+
+    @Bean
+    public KeyGenerator keyGenerator() {
+        return (target, method, params) -> {
+            StringBuilder sb = new StringBuilder();
+            // 类名
+            sb.append(target.getClass().getName());
+            // 方法名
+            sb.append(method.getName());
+            // 参数名
+            for (Object obj : params) {
+                sb.append(obj.toString());
+            }
+            return sb.toString();
+        };
+    }
+
+    /**
+     * 配置缓存管理器
+     *
+     * @param redisConnectionFactory
+     * @return
+     */
+    @Bean
+    @Primary
+    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
+        return RedisCacheManager
+                .builder(redisConnectionFactory)
+                .cacheDefaults(createConfiguration())
+                .build();
+    }
+
+    /**
+     * 实例化缓存配置
+     *
+     * @return
+     */
+    private RedisCacheConfiguration createConfiguration() {
+        ObjectMapper om = new ObjectMapper();
+        om.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+        // om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
+        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
+        // om.registerModule(new JavaTimeModule());
+        GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(om);
+        // redis缓存配置
+        RedisCacheConfiguration config = RedisCacheConfiguration
+                .defaultCacheConfig()
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(genericJackson2JsonRedisSerializer))
+                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
+                .disableCachingNullValues();
+        // 获取redis缓存配置属性
+        CacheProperties.Redis redisProperties = cacheProperties.getRedis();
+        // 设置缓存的默认超时时间:30分钟
+        if (redisProperties.getTimeToLive() != null) {
+            config = config.entryTtl(redisProperties.getTimeToLive());
+        }
+        // 禁用缓存空值
+        if (!redisProperties.isCacheNullValues()) {
+            config = config.disableCachingNullValues();
+        }
+        // 是否使用前缀
+        if (!redisProperties.isUseKeyPrefix()) {
+            config = config.disableKeyPrefix();
+        }
+        // 设置key的前缀
+        String prefix = Optional.ofNullable(redisProperties.getKeyPrefix()).orElse(name + ":");
+        if (!StringUtils.isEmpty(prefix)) {
+            config = config.prefixCacheNameWith(prefix);
+        }
+        return config;
+    }
+
+    // 缓存错误处理
+    @Bean
+    public CacheErrorHandler errorHandler() {
+        return new RedisCacheErrorHandler();
+    }
+
+    @Slf4j
+    private static class RedisCacheErrorHandler extends SimpleCacheErrorHandler {
+
+        @Override
+        public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
+            log.error("缓存获取异常:key=[{}]", key, exception);
+            super.handleCacheGetError(exception, cache, key);
+        }
+
+        @Override
+        public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
+            log.error("缓存写入异常:key=[{}]", key, exception);
+            super.handleCachePutError(exception, cache, key, value);
+        }
+
+        @Override
+        public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
+            log.error("缓存删除异常:key=[{}]", key, exception);
+            super.handleCacheEvictError(exception, cache, key);
+        }
+
+        @Override
+        public void handleCacheClearError(RuntimeException exception, Cache cache) {
+            log.error("缓存清除异常:", exception);
+            super.handleCacheClearError(exception, cache);
+        }
+    }
+}

+ 3 - 0
flow-common/flow-common-cache-starter/src/main/resources/META-INF/spring.factories

@@ -0,0 +1,3 @@
+# Auto Configure
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+com.flow.common.cache.configure.CacheConfiguration

+ 56 - 0
flow-common/flow-common-core/pom.xml

@@ -0,0 +1,56 @@
+<?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>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>flow-common-core</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--lombok-->
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
+        <!--guava-->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <!--web-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <!--oauth2-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-oauth2-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--mapStruct依赖-->
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct</artifactId>
+            <version>1.5.1.Final</version>
+        </dependency>
+        <dependency>
+            <groupId>org.mapstruct</groupId>
+            <artifactId>mapstruct-processor</artifactId>
+            <version>1.5.1.Final</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 143 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/configure/ConverterConfig.java

@@ -0,0 +1,143 @@
+package com.flow.common.core.configure;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.util.StringUtils;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+
+/**
+ * 用于转换RequestParam和PathVariable参数
+ */
+@Configuration
+public class ConverterConfig {
+    /**
+     * 日期正则表达式
+     */
+    private static final String DATE_REGEX = "[1-9]\\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])";
+
+    /**
+     * 时间正则表达式
+     */
+    private static final String TIME_REGEX = "(20|21|22|23|[0-1]\\d):[0-5]\\d:[0-5]\\d";
+
+    /**
+     * 日期和时间正则表达式
+     */
+    private static final String DATE_TIME_REGEX = DATE_REGEX + "\\s" + TIME_REGEX;
+
+    /**
+     * 13位时间戳正则表达式
+     */
+    private static final String TIME_STAMP_REGEX = "1\\d{12}";
+
+    /**
+     * 年和月正则表达式
+     */
+    private static final String YEAR_MONTH_REGEX = "[1-9]\\d{3}-(0[1-9]|1[0-2])";
+
+
+    /**
+     * Date转换器
+     * @return
+     */
+    @Bean
+    public Converter<String, Date> dateConverter() {
+        return new Converter<String, Date>() {
+            @Override
+            public Date convert(String source) {
+                if (StringUtils.isEmpty(source)) {
+                    return null;
+                }
+                // 时间戳
+                if (source.matches(TIME_STAMP_REGEX)) {
+                    return new Date(Long.parseLong(source));
+                }
+                DateFormat format;
+                if (source.matches(DATE_TIME_REGEX)) {
+                    // 日期时间
+                    format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                } else if (source.matches(DATE_REGEX)) {
+                    // 日期
+                    format = new SimpleDateFormat("yyyy-MM-dd");
+                } else if (source.matches(YEAR_MONTH_REGEX)) {
+                    // 年月
+                    format = new SimpleDateFormat("yyyy-MM");
+                } else {
+                    throw new IllegalArgumentException();
+                }
+                try {
+                    return format.parse(source);
+                } catch (ParseException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+    }
+
+    /**
+     * LocalDate转换器
+     * @return
+     */
+    @Bean
+    public Converter<String, LocalDate> localDateConverter() {
+        return new Converter<String, LocalDate>() {
+            @Override
+            public LocalDate convert(String source) {
+                return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+            }
+        };
+    }
+
+    /**
+     * LocalDateTime转换器
+     * @return
+     */
+    @Bean
+    public Converter<String, LocalDateTime> localDateTimeConverter() {
+        return new Converter<String, LocalDateTime>() {
+            @Override
+            public LocalDateTime convert(String source) {
+                return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+            }
+        };
+    }
+
+    /**
+     * LocalTime转换器
+     * @return
+     */
+    @Bean
+    public Converter<String, LocalTime> localTimeConverter() {
+        return new Converter<String, LocalTime>() {
+            @Override
+            public LocalTime convert(String source) {
+                return LocalTime.parse(source, DateTimeFormatter.ofPattern("HH:mm:ss"));
+            }
+        };
+    }
+
+    /**
+     * YearMonth转换器
+     * @return
+     */
+    @Bean
+    public Converter<String, YearMonth> yearMonthConverter() {
+        return new Converter<String, YearMonth>() {
+            @Override
+            public YearMonth convert(String source) {
+                return YearMonth.parse(source, DateTimeFormatter.ofPattern("yyyy-MM"));
+            }
+        };
+    }
+
+
+}

+ 66 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/configure/JacksonSerializerConfig.java

@@ -0,0 +1,66 @@
+package com.flow.common.core.configure;
+
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.YearMonthSerializer;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<>>
+ * @company 无名人
+ * @create 2021-04-23 16:31
+ */
+@Configuration
+public class JacksonSerializerConfig {
+
+    //全局序列化日期
+    @Bean
+    public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+        return builder -> {
+            // builder.serializerByType(BigInteger.class, ToStringSerializer.instance);
+            // builder.serializerByType(BigDecimal.class, ToStringSerializer.instance);
+            // 封装类型 Long ==> String
+            // builder.serializerByType(Long.class, ToStringSerializer.instance);
+            builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
+            /*--------------Date-------------------*/
+            builder.dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+            builder.timeZone(TimeZone.getTimeZone("GMT+8"));
+            builder.locale(Locale.CHINA);
+
+            /*--------------LocalDateTime-------------------*/
+            builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+            builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
+
+            /*--------------LocalDate-------------------*/
+            builder.serializerByType(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+            builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
+
+            /*--------------LocalTime-------------------*/
+            builder.serializerByType(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
+            builder.deserializerByType(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
+
+            /*--------------YearMonth-------------------*/
+            builder.serializerByType(YearMonth.class, new YearMonthSerializer(DateTimeFormatter.ofPattern("yyyy-MM")));
+            builder.deserializerByType(YearMonth.class, new YearMonthDeserializer(DateTimeFormatter.ofPattern("yyyy-MM")));
+        };
+    }
+}

+ 21 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/exception/BaseException.java

@@ -0,0 +1,21 @@
+package com.flow.common.core.exception;
+
+import lombok.Getter;
+import org.springframework.http.HttpStatus;
+
+/**
+ * 基本异常类
+ */
+@Getter
+public class BaseException extends RuntimeException{
+    private Integer status = HttpStatus.BAD_REQUEST.value();
+
+    public BaseException(String msg) {
+        super(msg);
+    }
+
+    public BaseException(HttpStatus status, String msg){
+        super(msg);
+        this.status = status.value();
+    }
+}

+ 102 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/exception/handler/BaseExceptionHandler.java

@@ -0,0 +1,102 @@
+package com.flow.common.core.exception.handler;
+
+import com.flow.common.core.exception.BaseException;
+import com.flow.common.core.model.Result;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.HttpMediaTypeNotSupportedException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Objects;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<全局异常捕获>>
+ * @company 个人学习
+ * @create 2020-11-04 10:30
+ */
+@Slf4j
+@RestControllerAdvice
+@Order(value = Ordered.HIGHEST_PRECEDENCE)
+public class BaseExceptionHandler {
+
+    /**
+     * Http请求方法不受支持异常
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(value = HttpRequestMethodNotSupportedException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
+        String message = "不支持Http请求方法";
+        log.error(message);
+        return Result.error(message);
+    }
+
+    @ExceptionHandler(value = HttpMediaTypeNotSupportedException.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
+        String message = "Http不支持的媒体类型";
+        log.error(message);
+        return Result.error(message);
+    }
+
+    /**
+     * 处理所有不可知的异常
+     */
+    @ExceptionHandler(Throwable.class)
+    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+    public Result handleException(Throwable e){
+        e.printStackTrace();
+        return Result.error("系统内部异常,请联系管理员");
+
+    }
+
+    /**
+     * 自定义异常处理
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(BaseException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result baseHandleException(BaseException e){
+        e.printStackTrace();
+        return Result.error(e.getMessage());
+    }
+
+
+    /**
+     * 参数异常拦截
+     *
+     * @param response
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(value = MethodArgumentNotValidException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result nullPointerExceptionHandler(HttpServletResponse response, MethodArgumentNotValidException e) {
+        String field = Objects.requireNonNull(e.getBindingResult().getFieldError()).getField();
+        String defaultMessage = e.getBindingResult().getFieldError().getDefaultMessage();
+        return Result.error(field + ":" + defaultMessage);
+    }
+
+    /**
+     * Assert错误拦截
+     *
+     * @param e
+     * @return
+     */
+    @ExceptionHandler(value = IllegalArgumentException.class)
+    @ResponseStatus(HttpStatus.BAD_REQUEST)
+    public Result IllegalArgumentException(IllegalArgumentException e) {
+        return Result.error(e.getMessage());
+    }
+}

+ 33 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/model/BaseMapper.java

@@ -0,0 +1,33 @@
+package com.flow.common.core.model;
+import java.util.List;
+
+public interface BaseMapper<D, E> {
+    /**
+     * DTO转Entity
+     * @param dto /
+     * @return /
+     */
+    E toEntity(D dto);
+
+    /**
+     * Entity转DTO
+     * @param entity /
+     * @return /
+     */
+    D toDto(E entity);
+
+    /**
+     * DTO集合转Entity集合
+     * @param dtoList /
+     * @return /
+     */
+    List<E> toEntity(List<D> dtoList);
+
+    /**
+     * Entity集合转DTO集合
+     * @param entityList /
+     * @return /
+     */
+    List <D> toDto(List<E> entityList);
+
+}

+ 18 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/model/CursorResult.java

@@ -0,0 +1,18 @@
+package com.flow.common.core.model;
+
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 游标分页结果
+ * @param <T>
+ */
+@Data
+public class CursorResult<T> {
+    @JsonSerialize(using = ToStringSerializer.class)
+    private Long cursor;
+    private List<T> rows;
+}

+ 28 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/model/PageResult.java

@@ -0,0 +1,28 @@
+package com.flow.common.core.model;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<分页>>
+ * @company 个人学习
+ * @create 2021-02-26 16:53
+ */
+@Data
+@NoArgsConstructor
+@Builder
+public class PageResult<T> {
+    private Long total;
+    private List<T> rows;
+
+    public PageResult(Long total, List<T> rows) {
+        this.total = total;
+        this.rows = rows;
+    }
+
+}

+ 9 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/model/Query.java

@@ -0,0 +1,9 @@
+package com.flow.common.core.model;
+
+import lombok.Data;
+
+@Data
+public class Query {
+    private int page = 1;
+    private int limit = 10;
+}

+ 205 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/model/Result.java

@@ -0,0 +1,205 @@
+package com.flow.common.core.model;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.flow.common.core.util.ApplicationContextUtil;
+import com.flow.common.core.util.StatusCode;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<>>
+ * @company cxf
+ * @create 2021-01-31 22:58
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class Result<T> {
+    //是否成功
+    private boolean success;
+    //状态码
+    private Integer code;
+    //信息
+    private String message;
+    //数据
+    private T data;
+
+    public Result(boolean success, Integer code, String message) {
+        this.success = success;
+        this.code = code;
+        this.message = message;
+    }
+
+    /**
+     * 返回成功
+     *
+     * @return
+     */
+    public static <T> Result<T> success() {
+        Result<T> response = new Result<>();
+        response.setSuccess(true);
+        response.setCode(StatusCode.SUCCESS);
+        response.setMessage("操作成功");
+        return response;
+    }
+
+    /**
+     * 返回成功
+     *
+     * @return
+     */
+    public static <T> Result<T> success(String message) {
+        Result<T> response = new Result<>();
+        response.setSuccess(true);
+        response.setCode(StatusCode.SUCCESS);
+        response.setMessage(message);
+        return response;
+    }
+
+    /**
+     * 返回成功
+     * @param data
+     * @return
+     * @param <T>
+     */
+    public static <T> Result<T> successData(T data) {
+        Result<T> response = new Result<>();
+        response.setSuccess(true);
+        response.setData(data);
+        response.setCode(StatusCode.SUCCESS);
+        return response;
+    }
+
+    /**
+     * 返回成功
+     *
+     * @param data 数据
+     * @return
+     */
+    public static <T> Result<T> success(T data) {
+        Result<T> response = new Result<>();
+        response.setSuccess(true);
+        response.setCode(StatusCode.SUCCESS);
+        response.setMessage("操作成功");
+        response.setData(data);
+        return response;
+    }
+
+    /**
+     * 返回成功
+     *
+     * @param data 数据
+     * @return
+     */
+    public static <T> Result<T> success(String message, T data) {
+        Result<T> response = new Result<>();
+        response.setSuccess(true);
+        response.setCode(StatusCode.SUCCESS);
+        response.setMessage(message);
+        response.setData(data);
+        return response;
+    }
+
+    /**
+     * 返回失败
+     *
+     * @return
+     */
+    public static <T> Result<T> error() {
+        Result<T> response = new Result<>();
+        response.setSuccess(false);
+        response.setCode(StatusCode.FAILURE);
+        response.setMessage("操作失败");
+        return response;
+    }
+
+    /**
+     * 返回失败
+     *
+     * @param data 数据
+     * @return
+     */
+    public static <T> Result<T> error(T data) {
+        Result<T> response = new Result<>();
+        response.setSuccess(false);
+        response.setCode(StatusCode.FAILURE);
+        response.setMessage("操作失败");
+        response.setData(data);
+        return response;
+    }
+
+    /**
+     * 返回失败
+     *
+     * @param message 信息
+     * @return
+     */
+    public static <T> Result<T> error(String message) {
+        Result<T> response = new Result<>();
+        response.setSuccess(false);
+        response.setCode(StatusCode.FAILURE);
+        response.setMessage(message);
+        return response;
+    }
+
+    /**
+     * 返回失败
+     *
+     * @param data 数据
+     * @return
+     */
+    public static <T> Result<T> error(String message, T data) {
+        Result<T> response = new Result<>();
+        response.setSuccess(false);
+        response.setCode(StatusCode.FAILURE);
+        response.setMessage(message);
+        response.setData(data);
+        return response;
+    }
+
+    /**
+     * 返回失败
+     *
+     * @param message 信息
+     * @return
+     */
+    public static <T> Result<T> error(String message, Integer code) {
+        Result<T> response = new Result<>();
+        response.setSuccess(false);
+        response.setCode(code);
+        response.setMessage(message);
+        return response;
+    }
+
+    /**
+     * 直接响应错误
+     *
+     * @param code
+     * @param message
+     * @throws IOException
+     */
+    public static void sendError(int code, String message) throws IOException {
+        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
+        response.setContentType("application/json;charset=UTF-8");
+        response.setStatus(code);
+        PrintWriter writer = response.getWriter();
+        Result result = Result.error(message, code);
+        ObjectMapper objectMapper = ApplicationContextUtil.getBean(ObjectMapper.class);
+        String json = objectMapper.writeValueAsString(result);
+        writer.write(json);
+        writer.flush();
+        writer.close();
+    }
+
+}

+ 52 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/util/ApplicationContextUtil.java

@@ -0,0 +1,52 @@
+package com.flow.common.core.util;
+
+import lombok.Getter;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<>>
+ * @company 无名人
+ * @create 2021-04-16 14:58
+ */
+@Configuration
+public class ApplicationContextUtil implements ApplicationContextAware {
+    @Getter
+    private static ApplicationContext applicationContext;
+
+    @Override
+    public void setApplicationContext(ApplicationContext context) throws BeansException {
+        ApplicationContextUtil.applicationContext = context;
+    }
+
+    public static <T> T getBean(Class<T> beanName) {
+        return applicationContext.getBean(beanName);
+    }
+
+    public static <T> List<T> getBeansOfType(Class<T> clazz) {
+        Map map;
+        try {
+            map = applicationContext.getBeansOfType(clazz);
+        } catch (Exception var3) {
+            map = null;
+        }
+        return map == null ? null : new ArrayList(map.values());
+    }
+
+    public static <T> Map<String, T> getBeans(Class<T> beanName){
+        return applicationContext.getBeansOfType(beanName);
+    }
+
+    public static Object getBean(String beanName) {
+        return applicationContext.getBean(beanName);
+    }
+
+}

+ 131 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/util/RequestUtils.java

@@ -0,0 +1,131 @@
+package com.flow.common.core.util;
+
+
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+public class RequestUtils {
+
+    public static HttpServletRequest getRequest() {
+        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+    }
+
+    public static String getIp() {
+        HttpServletRequest request = RequestUtils.getRequest();
+        // 获取请求主机IP地址,如果通过代理进来,则透过防火墙获取真实IP地址
+        String ip = request.getHeader("x-forwarded-for");
+        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("Proxy-Client-IP");
+            }
+            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("WL-Proxy-Client-IP");
+            }
+            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_CLIENT_IP");
+            }
+            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+            }
+            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+                ip = request.getRemoteAddr();
+            }
+            if (ip.contains(",")) {
+                ip = ip.split(",")[0];
+            }
+            if ("127.0.0.1".equals(ip)) {
+                // 获取本机真正的ip地址
+                try {
+                    ip = InetAddress.getLocalHost().getHostAddress();
+                } catch (UnknownHostException ignored) {
+                }
+            }
+
+        } else {
+            String[] ips = ip.split(",");
+            for (int index = 0; index < ips.length; index++) {
+                String strIp = ips[index];
+                if (!("unknown".equalsIgnoreCase(strIp))) {
+                    ip = strIp;
+                    break;
+                }
+            }
+        }
+        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
+    }
+
+    public static String getUserAgent() {
+        HttpServletRequest request = RequestUtils.getRequest();
+        return request.getHeader("User-Agent");
+    }
+
+    public static String getBrowser() {
+        HttpServletRequest request = RequestUtils.getRequest();
+        String uaStr = request.getHeader("User-Agent");
+        if (StringUtils.isEmpty(uaStr)) {
+            return "未知";
+        }
+        String browser = "";
+        if (uaStr.contains("MSIE") || uaStr.contains("Trident")) {
+            browser = "IE";
+        } else if (uaStr.contains("Firefox")) {
+            browser = "Firefox";
+        } else if (uaStr.contains("Chrome")) {
+            browser = "Chrome";
+        } else if (uaStr.contains("Opera")) {
+            browser = "Opera";
+        } else if (uaStr.contains("Safari")) {
+            browser = "Safari";
+        } else {
+            browser = "未知";
+        }
+        return browser;
+    }
+
+    public static String getOs() {
+        HttpServletRequest request = RequestUtils.getRequest();
+        String uaStr = request.getHeader("User-Agent");
+        if (StringUtils.isEmpty(uaStr)) {
+            return "未知";
+        }
+        String system = "";
+        if (uaStr.contains("Windows NT 10.0")) {
+            system = "Windows 10";
+        } else if (uaStr.contains("Windows NT 6.3")) {
+            system = "Windows 8.1";
+        } else if (uaStr.contains("Windows NT 6.2")) {
+            system = "Windows 8";
+        } else if (uaStr.contains("Windows NT 6.1")) {
+            system = "Windows 7";
+        } else if (uaStr.contains("Windows NT 6.0")) {
+            system = "Windows Vista";
+        } else if (uaStr.contains("Windows NT 5.1")) {
+            system = "Windows XP";
+        } else if (uaStr.contains("Windows NT 5.0")) {
+            system = "Windows 2000";
+        } else if (uaStr.contains("Mac")) {
+            system = "Mac";
+        } else if (uaStr.contains("iPhone")) {
+            system = "iPhone";
+        } else if (uaStr.contains("iPad")) {
+            system = "iPad";
+        } else if (uaStr.contains("iPod")) {
+            system = "iPod";
+        } else if (uaStr.contains("Android")) {
+            system = "Android";
+        } else if (uaStr.contains("X11")) {
+            system = "Unix";
+        } else if (uaStr.contains("Linux")) {
+            system = "Linux";
+        } else {
+            system = "其他";
+        }
+        return system;
+    }
+
+}

+ 56 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/util/SortingField.java

@@ -0,0 +1,56 @@
+package com.flow.common.core.util;
+
+import java.io.Serializable;
+
+/**
+ * 排序字段 DTO
+ *
+ * 类名加了 ing 的原因是,避免和 ES SortField 重名。
+ */
+public class SortingField implements Serializable {
+
+    /**
+     * 顺序 - 升序
+     */
+    public static final String ORDER_ASC = "asc";
+    /**
+     * 顺序 - 降序
+     */
+    public static final String ORDER_DESC = "desc";
+
+    /**
+     * 字段
+     */
+    private String field;
+    /**
+     * 顺序
+     */
+    private String order;
+
+    // 空构造方法,解决反序列化
+    public SortingField() {
+    }
+
+    public SortingField(String field, String order) {
+        this.field = field;
+        this.order = order;
+    }
+
+    public String getField() {
+        return field;
+    }
+
+    public SortingField setField(String field) {
+        this.field = field;
+        return this;
+    }
+
+    public String getOrder() {
+        return order;
+    }
+
+    public SortingField setOrder(String order) {
+        this.order = order;
+        return this;
+    }
+}

+ 19 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/util/StatusCode.java

@@ -0,0 +1,19 @@
+package com.flow.common.core.util;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<返回状态码>>
+ * @company 无名人
+ * @create 2021-04-22 14:05
+ */
+public class StatusCode {
+    public static final int OK=200;//成功
+    public static final int SUCCESS=200;//成功
+    public static final int FAILURE=400;//失败
+    public static final int ERROR=400;//失败
+    public static final int LOGIN_ERROR=401;//用户名或密码错误
+    public static final int ACCESS_ERROR=403;//权限不足
+    public static final int REMOTE_ERROR=404;//远程调用失败
+    public static final int REP_ERROR=405;//重复操作
+}

+ 20 - 0
flow-common/flow-common-core/src/main/java/com/flow/common/core/util/StrUtil.java

@@ -0,0 +1,20 @@
+package com.flow.common.core.util;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class StrUtil {
+
+    public static String format(String template, Map<?, ?> params) {
+        StringBuffer sb = new StringBuffer();
+        Matcher m = Pattern.compile("\\$\\{\\w+\\}").matcher(template);
+        while (m.find()) {
+            String param = m.group();
+            Object value = params.get(param.substring(2, param.length() - 1));
+            m.appendReplacement(sb, value == null ? "" : value.toString());
+        }
+        m.appendTail(sb);
+        return sb.toString();
+    }
+}

+ 41 - 0
flow-common/flow-common-doc-starter/pom.xml

@@ -0,0 +1,41 @@
+<?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>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>flow-common-doc-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--swagger-->
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-boot-starter</artifactId>
+            <version>3.0.3</version>
+        </dependency>
+        <!--starter-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+        <!--processor-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 92 - 0
flow-common/flow-common-doc-starter/src/main/java/com/flow/common/doc/configure/DocConfigure.java

@@ -0,0 +1,92 @@
+package com.flow.common.doc.configure;
+
+import com.flow.common.doc.properties.DocProperties;
+import com.google.common.collect.Lists;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiKey;
+import springfox.documentation.service.AuthorizationScope;
+import springfox.documentation.service.Contact;
+import springfox.documentation.service.SecurityReference;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spi.service.contexts.SecurityContext;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.List;
+
+/**
+ * Knife4j其实是对swagger2的二次封装而已
+ *
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<>>
+ * @company 个人学习
+ * @create 2021-02-02 17:42*/
+
+@Configuration
+@EnableSwagger2
+@EnableConfigurationProperties(DocProperties.class)
+@ConditionalOnProperty(value = "admin.doc.enable", havingValue = "true", matchIfMissing = true)
+public class DocConfigure {
+    private final DocProperties properties;
+    public DocConfigure(DocProperties properties) {
+        this.properties = properties;
+    }
+
+    @Bean
+    @Order(-1)
+    public Docket docket() {
+        //联系人
+        Contact contact = new Contact(
+                //联系人
+                properties.getName(),
+                //网址
+                properties.getUrl(),
+                //邮箱
+                properties.getEmail());
+
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(new ApiInfoBuilder()
+                        .title(properties.getTitle())
+                        .description(properties.getDescription())
+                        .termsOfServiceUrl(properties.getTermsOfServiceUrl())
+                        .contact(contact)
+                        .license(properties.getLicense())
+                        .version(properties.getVersion())
+                        .build())
+                //是否开启api文档
+                .enable(properties.getEnabled())
+                .select()
+                //这里指定Controller扫描包路径
+                .apis(RequestHandlerSelectors.any())
+                .paths(PathSelectors.any())
+                .build()
+
+                .securityContexts(Lists.newArrayList(securityContext())).securitySchemes(Lists.newArrayList(apiKey()));
+    }
+
+    private SecurityContext securityContext() {
+        return SecurityContext.builder()
+                .securityReferences(defaultAuth())
+                .forPaths(PathSelectors.regex("/.*"))
+                .build();
+    }
+
+    List<SecurityReference> defaultAuth() {
+        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
+        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
+        authorizationScopes[0] = authorizationScope;
+        return Lists.newArrayList(new SecurityReference("BearerToken", authorizationScopes));
+    }
+
+    private ApiKey apiKey() {
+        return new ApiKey("BearerToken", "Authorization", "header");
+    }
+}

+ 127 - 0
flow-common/flow-common-doc-starter/src/main/java/com/flow/common/doc/properties/DocProperties.java

@@ -0,0 +1,127 @@
+package com.flow.common.doc.properties;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+@ConfigurationProperties(prefix = "admin.doc")
+public class DocProperties {
+    /**
+     * 是否开启doc功能
+     */
+    private Boolean enabled = true;
+    /**
+     * 联系人
+     */
+    private String name;
+    /**
+     * 联系人url
+     */
+    private String url;
+    /**
+     * 联系方式:邮箱
+     */
+    private String email;
+    /**
+     * 标题
+     */
+    private String title;
+    /**
+     * 文档描述
+     */
+    private String description;
+    /**
+     * 服务url
+     */
+    private String termsOfServiceUrl;
+    /**
+     * 协议
+     */
+    private String license;
+    /**
+     * 协议地址
+     */
+    private String licenseUrl;
+    /**
+     * 版本
+     */
+    private String version;
+
+    public Boolean getEnabled() {
+        return enabled;
+    }
+
+    public void setEnabled(Boolean enabled) {
+        this.enabled = enabled;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public void setTitle(String title) {
+        this.title = title;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getTermsOfServiceUrl() {
+        return termsOfServiceUrl;
+    }
+
+    public void setTermsOfServiceUrl(String termsOfServiceUrl) {
+        this.termsOfServiceUrl = termsOfServiceUrl;
+    }
+
+    public String getLicense() {
+        return license;
+    }
+
+    public void setLicense(String license) {
+        this.license = license;
+    }
+
+    public String getLicenseUrl() {
+        return licenseUrl;
+    }
+
+    public void setLicenseUrl(String licenseUrl) {
+        this.licenseUrl = licenseUrl;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+}

+ 46 - 0
flow-common/flow-common-flowable-starter/pom.xml

@@ -0,0 +1,46 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-common-flowable-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <flowable.version>6.8.0</flowable.version>
+    </properties>
+
+    <dependencies>
+        <!-- flowable -->
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-spring-boot-starter-process</artifactId>
+            <version>${flowable.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-bpmn-converter</artifactId>
+            <version>${flowable.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-json-converter</artifactId>
+            <version>${flowable.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.flowable</groupId>
+            <artifactId>flowable-bpmn-layout</artifactId>
+            <version>${flowable.version}</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 47 - 0
flow-common/flow-common-flowable-starter/src/main/java/com/flow/flowable/configure/FlowableConfigure.java

@@ -0,0 +1,47 @@
+package com.flow.flowable.configure;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.flowable.common.engine.impl.cfg.IdGenerator;
+import org.flowable.common.engine.impl.persistence.deploy.DeploymentCache;
+import org.flowable.engine.impl.persistence.deploy.ProcessDefinitionCacheEntry;
+import org.flowable.spring.SpringProcessEngineConfiguration;
+import org.flowable.spring.boot.EngineConfigurationConfigurer;
+import org.flowable.task.service.impl.DefaultTaskPostProcessor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.PlatformTransactionManager;
+
+import java.util.Objects;
+
+@Configuration
+public class FlowableConfigure implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> {
+    @Autowired(required = false)
+    private PlatformTransactionManager transactionManager;
+    @Autowired(required = false)
+    private ObjectMapper objectMapper;
+    @Autowired(required = false)
+    private IdGenerator idGenerator;
+    @Autowired(required = false)
+    private DefaultTaskPostProcessor taskPostProcessor;
+    @Autowired(required = false)
+    private DeploymentCache<ProcessDefinitionCacheEntry> deploymentCache;
+
+    @Override
+    public void configure(SpringProcessEngineConfiguration engineConfiguration) {
+        if (Objects.nonNull(this.objectMapper)) {
+            engineConfiguration.setObjectMapper(objectMapper);
+        }
+        if (Objects.nonNull(this.idGenerator)) {
+            engineConfiguration.setIdGenerator(idGenerator);
+        }
+        if (Objects.nonNull(taskPostProcessor)) {
+            engineConfiguration.setTaskPostProcessor(taskPostProcessor);
+        }
+        if (Objects.nonNull(this.transactionManager)) {
+            engineConfiguration.setTransactionManager(transactionManager);
+        }
+        if (Objects.nonNull(this.deploymentCache)) {
+            engineConfiguration.setProcessDefinitionCache(deploymentCache);
+        }
+    }
+}

+ 48 - 0
flow-common/flow-common-mybatis-starter/pom.xml

@@ -0,0 +1,48 @@
+<?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>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>flow-common-mybatis-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--mysql-->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <!--mybatis plus-->
+        <dependency>
+            <groupId>com.baomidou</groupId>
+            <artifactId>mybatis-plus-boot-starter</artifactId>
+        </dependency>
+        <!--jackson-->
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--oauth2-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-oauth2-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 95 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/configure/MybatisPlusConfigure.java

@@ -0,0 +1,95 @@
+package com.flow.common.mybatis.configure;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.core.injector.AbstractMethod;
+import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;
+import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
+import com.baomidou.mybatisplus.extension.injector.methods.LogicDeleteBatchByIds;
+import com.baomidou.mybatisplus.extension.injector.methods.Upsert;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.flow.common.mybatis.handler.FieldMetaObjectHandler;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import java.util.List;
+
+@Configuration
+@EnableTransactionManagement
+public class MybatisPlusConfigure implements CommandLineRunner {
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Bean
+    @ConditionalOnMissingBean(value = DefaultSqlInjector.class)
+    public DefaultSqlInjector sqlInjector() {
+        return new DefaultSqlInjector() {
+            @Override
+            public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
+                List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
+                methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
+                methodList.add(new AlwaysUpdateSomeColumnById());
+                methodList.add(new LogicDeleteBatchByIds());
+                methodList.add(new Upsert());
+                return methodList;
+            }
+        };
+    }
+
+    /**
+     * 注册插件
+     *
+     * @return
+     */
+    @Bean
+    public MybatisPlusInterceptor mybatisPlusInterceptor() {
+        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
+        // 分页插件
+        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+        // 乐观锁
+        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+        // 防止全表更新与删除
+        mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+        // 动态表名
+        /*DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
+        dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName)->{
+            if("cte".equals(tableName)){
+                return tableName;
+            }
+            return "xf_admin."+tableName;
+        });
+        mybatisPlusInterceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);*/
+        return mybatisPlusInterceptor;
+    }
+
+    /**
+     * 字段填充
+     *
+     * @return
+     */
+    @Bean
+    @ConditionalOnMissingBean(value = FieldMetaObjectHandler.class)
+    public FieldMetaObjectHandler fieldMetaObjectHandler() {
+        return new FieldMetaObjectHandler();
+    }
+
+    /**
+     * 设置json类型处理器
+     *
+     * @param args
+     */
+    @Override
+    public void run(String... args) {
+        JacksonTypeHandler.setObjectMapper(objectMapper);
+    }
+}

+ 6 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/constant/SqlConstant.java

@@ -0,0 +1,6 @@
+package com.flow.common.mybatis.constant;
+
+public interface SqlConstant {
+    String LIMIT1 = "LIMIT 1";
+    String LIMIT2 = "LIMIT 2";
+}

+ 41 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/dao/BaseDao.java

@@ -0,0 +1,41 @@
+package com.flow.common.mybatis.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
+import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
+import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
+import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
+import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface BaseDao<T> extends BaseMapper<T> {
+    /**
+     * 以下定义的 4个 default method, copy from {@link com.baomidou.mybatisplus.extension.toolkit.ChainWrappers}
+     */
+    default QueryChainWrapper<T> queryChain() {
+        return new QueryChainWrapper<>(this);
+    }
+
+    default LambdaQueryChainWrapper<T> lambdaQueryChain() {
+        return new LambdaQueryChainWrapper<>(this);
+    }
+
+    default UpdateChainWrapper<T> updateChain() {
+        return new UpdateChainWrapper<>(this);
+    }
+
+    default LambdaUpdateChainWrapper<T> lambdaUpdateChain() {
+        return new LambdaUpdateChainWrapper<>(this);
+    }
+
+    /**
+     * 以下定义的 4个 method 其中 3 个是内置的选装件
+     */
+    int insertBatchSomeColumn(List<T> entityList);
+
+    int alwaysUpdateSomeColumnById(@Param(Constants.ENTITY) T entity);
+
+    int deleteByIdWithFill(T entity);
+}

+ 87 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/entity/BaseEntity.java

@@ -0,0 +1,87 @@
+package com.flow.common.mybatis.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+public class BaseEntity implements Serializable {
+    /**
+     * id
+     */
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    /**
+     * 创建者
+     */
+    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+    @TableField(fill = FieldFill.INSERT)
+    private String createdBy;
+
+    /**
+     * 更新者
+     */
+    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+    @TableField(fill = FieldFill.UPDATE)
+    private String updatedBy;
+
+    /**
+     * 创建日期
+     */
+    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    /**
+     * 更新时间
+     */
+    @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+    @TableField(fill = FieldFill.UPDATE)
+    private LocalDateTime updateTime;
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public String getUpdatedBy() {
+        return updatedBy;
+    }
+
+    public void setUpdatedBy(String updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public LocalDateTime getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(LocalDateTime createTime) {
+        this.createTime = createTime;
+    }
+
+    public LocalDateTime getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(LocalDateTime updateTime) {
+        this.updateTime = updateTime;
+    }
+}

+ 32 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/entity/FilterCondition.java

@@ -0,0 +1,32 @@
+package com.flow.common.mybatis.entity;
+
+
+public class FilterCondition {
+    private String field;
+    private String operator;
+    private Object value;
+
+    public String getField() {
+        return field;
+    }
+
+    public void setField(String field) {
+        this.field = field;
+    }
+
+    public String getOperator() {
+        return operator;
+    }
+
+    public void setOperator(String operator) {
+        this.operator = operator;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+}

+ 35 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/entity/FilterGroup.java

@@ -0,0 +1,35 @@
+package com.flow.common.mybatis.entity;
+
+
+import java.util.List;
+
+
+public class FilterGroup {
+    private String operator;
+    private List<FilterCondition> conditions;
+    private List<FilterGroup> groups;
+
+    public String getOperator() {
+        return operator;
+    }
+
+    public void setOperator(String operator) {
+        this.operator = operator;
+    }
+
+    public List<FilterCondition> getConditions() {
+        return conditions;
+    }
+
+    public void setConditions(List<FilterCondition> conditions) {
+        this.conditions = conditions;
+    }
+
+    public List<FilterGroup> getGroups() {
+        return groups;
+    }
+
+    public void setGroups(List<FilterGroup> groups) {
+        this.groups = groups;
+    }
+}

+ 49 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/handler/FieldMetaObjectHandler.java

@@ -0,0 +1,49 @@
+package com.flow.common.mybatis.handler;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.flow.common.oauth2.utils.SecurityContextUtil;
+import org.apache.ibatis.reflection.MetaObject;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author caixiaofeng
+ * @version 1.0
+ * @Description: <<字段填充>>
+ * @company 无名人
+ * @create 2021-02-22 10:03
+ */
+public class FieldMetaObjectHandler implements MetaObjectHandler {
+
+    /**
+     * 插入时填充
+     *
+     * @param metaObject
+     */
+    @Override
+    public void insertFill(MetaObject metaObject) {
+        // 实体类中有该属性时调用
+        if (metaObject.hasSetter("createTime")) {
+            this.fillStrategy(metaObject, "createTime", LocalDateTime.now());
+        }
+        if (metaObject.hasSetter("createdBy")) {
+            this.fillStrategy(metaObject, "createdBy", SecurityContextUtil.getUserId());
+        }
+    }
+
+    /**
+     * 更新时填充
+     *
+     * @param metaObject
+     */
+    @Override
+    public void updateFill(MetaObject metaObject) {
+        // 实体类中有该属性时调用
+        if (metaObject.hasSetter("updateTime")) {
+            this.fillStrategy(metaObject, "updateTime", LocalDateTime.now());
+        }
+        if (metaObject.hasSetter("updatedBy")) {
+            this.fillStrategy(metaObject, "updatedBy", SecurityContextUtil.getUserId());
+        }
+    }
+}

+ 51 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/service/BaseService.java

@@ -0,0 +1,51 @@
+package com.flow.common.mybatis.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.flow.common.mybatis.entity.FilterCondition;
+import com.flow.common.mybatis.entity.FilterGroup;
+
+import java.util.List;
+
+/**
+ * 基础服务接口,所有Service接口都要继承
+ *
+ * @param <T>
+ */
+public interface BaseService<T> extends IService<T> {
+
+
+    default QueryWrapper<T> buildQuery(FilterGroup filterGroup) {
+        QueryWrapper<T> queryWrapper = new QueryWrapper<>();
+        buildQuery(queryWrapper, filterGroup);
+        List<FilterGroup> groups = filterGroup.getGroups();
+        if (CollectionUtils.isNotEmpty(groups)) {
+            groups.forEach(group -> buildQuery(queryWrapper, group));
+        }
+        return queryWrapper;
+    }
+
+    default void buildQuery(QueryWrapper<T> queryWrapper, FilterGroup filterGroup) {
+        List<FilterCondition> conditions = filterGroup.getConditions();
+        if (CollectionUtils.isNotEmpty(conditions)) {
+            conditions.forEach(condition -> {
+                String field = StringUtils.camelToUnderline(condition.getField());
+                String operator = condition.getOperator();
+                Object value = condition.getValue();
+                switch (operator) {
+                    case "eq":
+                        queryWrapper.eq(field, value);
+                        break;
+                    case "ne":
+                        queryWrapper.ne(field, value);
+                        break;
+                    case "gt":
+                        queryWrapper.gt(field, value);
+                        break;
+                }
+            });
+        }
+    }
+}

+ 16 - 0
flow-common/flow-common-mybatis-starter/src/main/java/com/flow/common/mybatis/service/impl/BaseServiceImpl.java

@@ -0,0 +1,16 @@
+package com.flow.common.mybatis.service.impl;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.flow.common.mybatis.service.BaseService;
+
+
+/**
+ * 基础服务类,所有Service都要继承
+ *
+ * @param <M>
+ * @param <T>
+ */
+public class BaseServiceImpl<M extends BaseMapper<T>, T> extends ServiceImpl<M, T> implements BaseService<T> {
+
+}

+ 53 - 0
flow-common/flow-common-oauth2-starter/pom.xml

@@ -0,0 +1,53 @@
+<?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>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>flow-common-oauth2-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--<dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>-->
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security.oauth</groupId>
+            <artifactId>spring-security-oauth2</artifactId>
+            <version>2.3.8.RELEASE</version>
+        </dependency>
+        <!--<dependency>
+            &lt;!&ndash;https://juejin.cn/post/6992133498920239140?searchId=202403011720484B2DC9E976F9007B60EE&ndash;&gt;
+            <groupId>org.springframework.security.oauth.boot</groupId>
+            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
+            <version>2.5.2</version>
+        </dependency>-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-redis-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 42 - 0
flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/AuthenticationKeyGenerator.java

@@ -0,0 +1,42 @@
+package com.flow.common.oauth2.configure;
+
+import org.springframework.security.oauth2.common.util.OAuth2Utils;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.OAuth2Request;
+import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeSet;
+import java.util.UUID;
+
+/**
+ * 重写token的key生成器
+ */
+public class AuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
+    private static final String CLIENT_ID = "client_id";
+    private static final String SCOPE = "scope";
+    private static final String USERNAME = "username";
+
+    @Override
+    public String extractKey(OAuth2Authentication authentication) {
+         Map<String, String> values = new LinkedHashMap<String, String>();
+        OAuth2Request authorizationRequest = authentication.getOAuth2Request();
+        if (!authentication.isClientOnly()) {
+            values.put(USERNAME, authentication.getName());
+        }
+        values.put(CLIENT_ID, authorizationRequest.getClientId());
+        if (authorizationRequest.getScope() != null) {
+            values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<String>(authorizationRequest.getScope())));
+        }
+        // 添加uuid解决每次生成的 token都一样的问题
+        values.put("uuid", UUID.randomUUID().toString());
+        // 多地登录不同token可以设备设备id或ip地址
+        // String deviceId = authorizationRequest.getRequestParameters().get("deviceId");
+        // values.put("deviceId", deviceId);
+
+        // 如果是多租户系统,这里要区分租户ID 条件
+        // values.put("tenantId", "我是租户id");
+        return generateKey(values);
+    }
+}

+ 219 - 0
flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/AuthorizationServerConfigure.java

@@ -0,0 +1,219 @@
+package com.flow.common.oauth2.configure;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Primary;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.ClientDetailsService;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
+import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
+import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices;
+import org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter;
+import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
+import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
+import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider;
+
+import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+@Configuration
+@EnableAuthorizationServer
+public class AuthorizationServerConfigure extends AuthorizationServerConfigurerAdapter {
+    private final AuthenticationManager authenticationManager;
+    private final UserDetailsService userDetailsService;
+    private final ClientDetailsService clientDetailsService;
+    private final DataSource dataSource;
+    private final RedisConnectionFactory redisConnectionFactory;
+
+    public AuthorizationServerConfigure(AuthenticationManager authenticationManager,
+                                        UserDetailsService userDetailsService,
+                                        ClientDetailsService clientDetailsService,
+                                        DataSource dataSource,
+                                        RedisConnectionFactory redisConnectionFactory) {
+        this.authenticationManager = authenticationManager;
+        this.userDetailsService = userDetailsService;
+        this.clientDetailsService = clientDetailsService;
+        this.dataSource = dataSource;
+        this.redisConnectionFactory = redisConnectionFactory;
+    }
+
+    /**
+     * oauth2的客户端配置
+     *
+     * @param clients
+     * @throws Exception
+     */
+    @Override
+    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+       /* clients.inMemory()
+                .withClient("client")
+                .secret(passwordEncoder.encode("security_secret"))
+                .authorizedGrantTypes("refresh_token","password","authorization_code")
+                .scopes("all")
+                .resourceIds("flow-app")
+                .redirectUris("https://www.baidu.com/callback");*/
+        clients.withClientDetails(clientDetailsService);
+    }
+
+    /**
+     * 令牌端点的安全配置
+     *
+     * @param security
+     */
+    @Override
+    public void configure(AuthorizationServerSecurityConfigurer security) {
+        security
+                .tokenKeyAccess("permitAll()")
+                .checkTokenAccess("permitAll()")
+                .allowFormAuthenticationForClients();
+    }
+
+    /**
+     * 配置授权
+     *
+     * @param endpoints
+     * @throws Exception
+     */
+    @Override
+    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+        endpoints
+                //认证管理器
+                .authenticationManager(authenticationManager)
+                //用户信息
+                .userDetailsService(userDetailsService)
+                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
+                // 授权码管理策略,针对授权码模式有效,会将授权码放到 auth_code 表,授权后就会删除它
+                .authorizationCodeServices(jdbcAuthorizationCodeServices());
+        // 使用redis的token方式
+        endpoints
+                //token存储
+                .tokenStore(tokenStore())
+                // 添加令牌增强器
+                .tokenEnhancer(customTokenEnhancer());
+
+
+        // 自定义异常转换类(替换oauth2默认返回格式,改成AjaxResponse统一格式)
+        // endpoints.exceptionTranslator(responseExceptionTranslator);
+    }
+
+    /**
+     * 授权码管理策略
+     *
+     * @return
+     */
+    @Bean
+    public AuthorizationCodeServices jdbcAuthorizationCodeServices() {
+        return new JdbcAuthorizationCodeServices(dataSource);
+    }
+
+    /**
+     * redis存储token
+     *
+     * @return
+     */
+    @Bean
+    @ConditionalOnMissingBean(value = TokenStore.class)
+    public TokenStore tokenStore() {
+        RedisTokenStore tokenStore = new RedisTokenStore(redisConnectionFactory);
+        tokenStore.setAuthenticationKeyGenerator(new AuthenticationKeyGenerator());
+        return tokenStore;
+    }
+
+    @Primary
+    @Bean
+    public DefaultTokenServices tokenServices() {
+        DefaultTokenServices tokenServices = new DefaultTokenServices() {
+            @Override
+            public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
+                // 如果用户在其他地方已经登录,就踢下线
+                /*OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
+                if (existingAccessToken != null) {
+                    tokenStore.removeAccessToken(existingAccessToken);
+                    if (existingAccessToken.getRefreshToken() != null) {
+                        tokenStore.removeRefreshToken(existingAccessToken.getRefreshToken());
+                    }
+                }*/
+                return super.createAccessToken(authentication);
+            }
+        };
+        // 请求回来的token都会变.但是请求的refresh token不会续期(false)或者重置为初始化时间(true)
+        tokenServices.setReuseRefreshToken(true);
+        // 支持刷新令牌(默认false)
+        tokenServices.setSupportRefreshToken(true);
+        tokenServices.setClientDetailsService(clientDetailsService);
+        // token加强链
+        //TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
+        //tokenEnhancerChain.setTokenEnhancers(Lists.newArrayList(tokenEnhancer()));
+        tokenServices.setTokenEnhancer(customTokenEnhancer());
+        // token存储
+        tokenServices.setTokenStore(tokenStore());
+
+        if (userDetailsService != null) {
+            PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider();
+            provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<>(userDetailsService));
+            ArrayList<AuthenticationProvider> list = new ArrayList<>();
+            list.add(provider);
+            tokenServices.setAuthenticationManager(new ProviderManager(list));
+        }
+        return tokenServices;
+    }
+
+    /**
+     * token增强器
+     *
+     * @return
+     */
+    @Bean
+    public TokenEnhancer customTokenEnhancer() {
+        return new TokenEnhancer() {
+            @Override
+            public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+                if (accessToken instanceof DefaultOAuth2AccessToken) {
+                    // 添加附加信息
+                    Map<String, Object> additionalInformation = new HashMap<>();
+                    additionalInformation.put("clientId", authentication.getOAuth2Request().getClientId());
+                    additionalInformation.put("resourceIds", authentication.getOAuth2Request().getResourceIds());
+                    ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);
+                }
+                return accessToken;
+            }
+        };
+    }
+
+    /**
+     * 默认密码模式配置  要不要都行.主要是给社交登录获取该bean使用使用
+     *
+     * @return
+     */
+    @Bean
+    public ResourceOwnerPasswordTokenGranter resourceOwnerPasswordTokenGranter(OAuth2RequestFactory oAuth2RequestFactory) {
+        return new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices(), clientDetailsService, oAuth2RequestFactory);
+    }
+
+    @Bean
+    public DefaultOAuth2RequestFactory oAuth2RequestFactory() {
+        return new DefaultOAuth2RequestFactory(clientDetailsService);
+    }
+
+}

+ 57 - 0
flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/ResourceServerConfigure.java

@@ -0,0 +1,57 @@
+package com.flow.common.oauth2.configure;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.security.web.access.AccessDeniedHandler;
+
+@EnableResourceServer
+@EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true, securedEnabled = true, jsr250Enabled = true)
+@EnableAutoConfiguration(exclude = UserDetailsServiceAutoConfiguration.class)
+public class ResourceServerConfigure extends ResourceServerConfigurerAdapter {
+    @Autowired(required = false)
+    private AccessDeniedHandler accessDeniedHandler;
+    @Autowired(required = false)
+    private AuthenticationEntryPoint authenticationEntryPoint;
+
+    @Override
+    public void configure(HttpSecurity http) throws Exception {
+        http     //关闭csrf保护
+                .csrf().disable()
+                //不使用session
+                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+                .and()
+                // 认证请求
+                .authorizeRequests()
+                //放行预请求
+                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
+                .antMatchers("/static/file/**").permitAll()
+                .antMatchers("/auth/login", "/auth/code", "/auth/logout", "/user/list", "/model/download").permitAll()
+                // websocket路径放行
+                .antMatchers("/socket/**").permitAll()
+                // 其他所有请求都需要认证
+                .anyRequest().authenticated();
+    }
+
+    @Override
+    public void configure(ResourceServerSecurityConfigurer resources) {
+        // 必须要要AuthorizationServer的资源编号保持一致
+        resources.resourceId("flow-app");
+        // 身份验证入口点
+        if (authenticationEntryPoint != null) {
+            resources.authenticationEntryPoint(authenticationEntryPoint);
+        }
+        // 拒绝访问处理程序
+        if (accessDeniedHandler != null) {
+            resources.accessDeniedHandler(accessDeniedHandler);
+        }
+    }
+}

+ 53 - 0
flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/configure/SecurityConfigure.java

@@ -0,0 +1,53 @@
+package com.flow.common.oauth2.configure;
+
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.HttpMethod;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.BeanIds;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.WebSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+@Order(99)
+@Configuration
+@EnableWebSecurity
+public class SecurityConfigure extends WebSecurityConfigurerAdapter {
+    private final UserDetailsService userDetailsService;
+
+    public SecurityConfigure(UserDetailsService userDetailsService) {
+        this.userDetailsService = userDetailsService;
+    }
+
+
+    @Override
+    public void configure(WebSecurity web) {
+        web.ignoring()
+                .antMatchers(HttpMethod.OPTIONS, "/**");
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
+    }
+
+
+    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
+    @Override
+    public AuthenticationManager authenticationManagerBean() throws Exception {
+        return super.authenticationManagerBean();
+    }
+
+    @Bean
+    @ConditionalOnMissingBean(value = PasswordEncoder.class)
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+}

+ 70 - 0
flow-common/flow-common-oauth2-starter/src/main/java/com/flow/common/oauth2/utils/SecurityContextUtil.java

@@ -0,0 +1,70 @@
+package com.flow.common.oauth2.utils;
+
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class SecurityContextUtil {
+
+    /**
+     * 获得当前登录用户ID
+     *
+     * @return
+     */
+    public static String getUserId() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (Objects.nonNull(authentication)) {
+            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
+            return userDetails.getUsername();
+        }
+        return null;
+    }
+
+    /**
+     * 登录用户信息
+     * @return
+     */
+    public static UserDetails getUserInfo(){
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication != null) {
+            return (UserDetails) authentication.getPrincipal();
+        }
+        return null;
+    }
+
+    /**
+     * 获得当前登录用户Token
+     * @return
+     */
+    public static String getAccessToken(){
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication != null) {
+            OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
+            return details.getTokenValue();
+        }
+        return null;
+    }
+
+
+    /**
+     * 获取当前登录用户的权限
+     * @return
+     */
+    public static List<String> getCurrentAuthorities(){
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        if (authentication != null) {
+            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
+            return authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList());
+        }
+        return new ArrayList<>();
+    }
+
+}

+ 46 - 0
flow-common/flow-common-redis-starter/pom.xml

@@ -0,0 +1,46 @@
+<?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>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>flow-common-redis-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <!--core-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <!--redis-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+        <!--pool2连接池-->
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-pool2</artifactId>
+        </dependency>
+        <!--processor-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-configuration-processor</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+</project>

+ 43 - 0
flow-common/flow-common-redis-starter/src/main/java/com/flow/common/redis/configure/LettuceRedisConfigure.java

@@ -0,0 +1,43 @@
+package com.flow.common.redis.configure;
+
+import com.flow.common.redis.service.RedisService;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisOperations;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+public class LettuceRedisConfigure {
+
+    @Bean(name = "redisTemplate")
+    @ConditionalOnClass(RedisOperations.class)
+    public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory factory) {
+        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
+        //设置工厂
+        redisTemplate.setConnectionFactory(factory);
+        // Key采用String序列化
+        redisTemplate.setKeySerializer(new StringRedisSerializer());
+        // Hash的Key采用String序列化方式
+        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
+        // Value采用jackson的json序列化方式
+        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
+        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
+        // HashValue采用jackson的json序列化方式
+        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
+        //属性设置后
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    @Bean
+    @ConditionalOnBean(name = "redisTemplate")
+    public RedisService redisService() {
+        return new RedisService();
+    }
+
+}

+ 584 - 0
flow-common/flow-common-redis-starter/src/main/java/com/flow/common/redis/service/RedisService.java

@@ -0,0 +1,584 @@
+package com.flow.common.redis.service;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+@SuppressWarnings("all")
+public class RedisService {
+    private Logger log = LoggerFactory.getLogger(this.getClass());
+
+    @Autowired
+    private RedisTemplate<String, Object> redisTemplate;
+
+    /**
+     * 发布消息
+     *
+     * @param queueName
+     * @param message
+     */
+    public void sendMessage(String queueName, Object message) {
+        redisTemplate.opsForList().leftPush(queueName, message);
+    }
+
+    /**
+     * 订阅消息
+     *
+     * @param queueName
+     * @return
+     */
+    public Object receiveMessage(String queueName) {
+        return redisTemplate.opsForList().rightPop(queueName);
+    }
+
+
+    /**
+     * 指定缓存失效时间
+     *
+     * @param key  键
+     * @param time 时间(秒)
+     * @return Boolean
+     */
+    public Boolean expire(String key, Long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.expire(key, time, TimeUnit.SECONDS);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 根据key获取过期时间
+     *
+     * @param key 键 不能为 null
+     * @return 时间(秒) 返回 0代表为永久有效
+     */
+    public Long getExpire(String key) {
+        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
+    }
+
+    /**
+     * 判断 key是否存在
+     *
+     * @param key 键
+     * @return true 存在 false不存在
+     */
+    public Boolean hasKey(String key) {
+        try {
+            return redisTemplate.hasKey(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除缓存
+     *
+     * @param key 可以传一个值 或多个
+     */
+    public void del(String... key) {
+        if (key != null && key.length > 0) {
+            if (key.length == 1) {
+                redisTemplate.delete(key[0]);
+            } else {
+                redisTemplate.delete(Arrays.asList(key));
+            }
+        }
+    }
+
+    /**
+     * 普通缓存获取
+     *
+     * @param key 键
+     * @return 值
+     */
+    public Object get(String key) {
+        return key == null ? null : redisTemplate.opsForValue().get(key);
+    }
+
+    /**
+     * 普通缓存放入
+     *
+     * @param key   键
+     * @param value 值
+     * @return true成功 false失败
+     */
+    public Boolean set(String key, Object value) {
+        try {
+            redisTemplate.opsForValue().set(key, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 普通缓存放入并设置时间
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
+     * @return true成功 false 失败
+     */
+    public Boolean set(String key, Object value, Long time) {
+        try {
+            if (time > 0) {
+                redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
+            } else {
+                set(key, value);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 递增
+     *
+     * @param key   键
+     * @param delta 要增加几(大于0)
+     * @return Long
+     */
+    public Long incr(String key, Long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递增因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, delta);
+    }
+
+
+    /**
+     * 递减
+     *
+     * @param key   键
+     * @param delta 要减少几
+     * @return Long
+     */
+    public Long decr(String key, Long delta) {
+        if (delta < 0) {
+            throw new RuntimeException("递减因子必须大于0");
+        }
+        return redisTemplate.opsForValue().increment(key, -delta);
+    }
+
+    /**
+     * HashGet
+     *
+     * @param key  键 不能为 null
+     * @param item 项 不能为 null
+     * @return 值
+     */
+    public Object hget(String key, String item) {
+        return redisTemplate.opsForHash().get(key, item);
+    }
+
+    /**
+     * 获取 hashKey对应的所有键值
+     *
+     * @param key 键
+     * @return 对应的多个键值
+     */
+    public Map<Object, Object> hmget(String key) {
+        return redisTemplate.opsForHash().entries(key);
+    }
+
+    /**
+     * HashSet
+     *
+     * @param key 键
+     * @param map 对应多个键值
+     * @return true 成功 false 失败
+     */
+    public Boolean hmset(String key, Map<String, Object> map) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * HashSet 并设置时间
+     *
+     * @param key  键
+     * @param map  对应多个键值
+     * @param time 时间(秒)
+     * @return true成功 false失败
+     */
+    public Boolean hmset(String key, Map<String, Object> map, Long time) {
+        try {
+            redisTemplate.opsForHash().putAll(key, map);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @return true 成功 false失败
+     */
+    public Boolean hset(String key, String item, Object value) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 向一张hash表中放入数据,如果不存在将创建
+     *
+     * @param key   键
+     * @param item  项
+     * @param value 值
+     * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
+     * @return true 成功 false失败
+     */
+    public Boolean hset(String key, String item, Object value, Long time) {
+        try {
+            redisTemplate.opsForHash().put(key, item, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 删除hash表中的值
+     *
+     * @param key  键 不能为 null
+     * @param item 项 可以使多个不能为 null
+     */
+    public void hdel(String key, Object... item) {
+        redisTemplate.opsForHash().delete(key, item);
+    }
+
+    /**
+     * 判断hash表中是否有该项的值
+     *
+     * @param key  键 不能为 null
+     * @param item 项 不能为 null
+     * @return true 存在 false不存在
+     */
+    public Boolean hHasKey(String key, String item) {
+        return redisTemplate.opsForHash().hasKey(key, item);
+    }
+
+
+    /**
+     * hash递增 如果不存在,就会创建一个 并把新增后的值返回
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要增加几(大于0)
+     * @return Double
+     */
+    public Double hincr(String key, String item, Double by) {
+        return redisTemplate.opsForHash().increment(key, item, by);
+    }
+
+    /**
+     * hash递减
+     *
+     * @param key  键
+     * @param item 项
+     * @param by   要减少记(小于0)
+     * @return Double
+     */
+    public Double hdecr(String key, String item, Double by) {
+        return redisTemplate.opsForHash().increment(key, item, -by);
+    }
+
+    /**
+     * 根据 key获取 Set中的所有值
+     *
+     * @param key 键
+     * @return Set
+     */
+    public Set<Object> sGet(String key) {
+        try {
+            return redisTemplate.opsForSet().members(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 根据value从一个set中查询,是否存在
+     *
+     * @param key   键
+     * @param value 值
+     * @return true 存在 false不存在
+     */
+    public Boolean sHasKey(String key, Object value) {
+        try {
+            return redisTemplate.opsForSet().isMember(key, value);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将数据放入set缓存
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public Long sSet(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().add(key, values);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0L;
+        }
+    }
+
+
+    /**
+     * 将set数据放入缓存
+     *
+     * @param key    键
+     * @param time   时间(秒)
+     * @param values 值 可以是多个
+     * @return 成功个数
+     */
+    public Long sSetAndTime(String key, Long time, Object... values) {
+        try {
+            Long count = redisTemplate.opsForSet().add(key, values);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return count;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0L;
+        }
+    }
+
+    /**
+     * 获取set缓存的长度
+     *
+     * @param key 键
+     * @return Long
+     */
+    public Long sGetSetSize(String key) {
+        try {
+            return redisTemplate.opsForSet().size(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0L;
+        }
+    }
+
+    /**
+     * 移除值为value的
+     *
+     * @param key    键
+     * @param values 值 可以是多个
+     * @return 移除的个数
+     */
+    public Long setRemove(String key, Object... values) {
+        try {
+            return redisTemplate.opsForSet().remove(key, values);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0L;
+        }
+    }
+
+    /**
+     * 获取list缓存的内容
+     *
+     * @param key   键
+     * @param start 开始
+     * @param end   结束 0 到 -1代表所有值
+     * @return List
+     */
+    public List<Object> lGet(String key, Long start, Long end) {
+        try {
+            return redisTemplate.opsForList().range(key, start, end);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 获取list缓存的长度
+     *
+     * @param key 键
+     * @return Long
+     */
+    public Long lGetListSize(String key) {
+        try {
+            return redisTemplate.opsForList().size(key);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0L;
+        }
+    }
+
+    /**
+     * 通过索引 获取list中的值
+     *
+     * @param key   键
+     * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;
+     *              index<0时,-1,表尾,-2倒数第二个元素,依次类推
+     * @return Object
+     */
+    public Object lGetIndex(String key, Long index) {
+        try {
+            return redisTemplate.opsForList().index(key, index);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return Boolean
+     */
+    public Boolean lSet(String key, Object value) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return Boolean
+     */
+    public Boolean lSet(String key, Object value, Long time) {
+        try {
+            redisTemplate.opsForList().rightPush(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @return Boolean
+     */
+    public Boolean lSet(String key, List<Object> value) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 将list放入缓存
+     *
+     * @param key   键
+     * @param value 值
+     * @param time  时间(秒)
+     * @return Boolean
+     */
+    public Boolean lSet(String key, List<Object> value, Long time) {
+        try {
+            redisTemplate.opsForList().rightPushAll(key, value);
+            if (time > 0) {
+                expire(key, time);
+            }
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 根据索引修改list中的某条数据
+     *
+     * @param key   键
+     * @param index 索引
+     * @param value 值
+     * @return Boolean
+     */
+    public Boolean lUpdateIndex(String key, Long index, Object value) {
+        try {
+            redisTemplate.opsForList().set(key, index, value);
+            return true;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return false;
+        }
+    }
+
+    /**
+     * 移除N个值为value
+     *
+     * @param key   键
+     * @param count 移除多少个
+     * @param value 值
+     * @return 移除的个数
+     */
+    public Long lRemove(String key, Long count, Object value) {
+        try {
+            return redisTemplate.opsForList().remove(key, count, value);
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+            return 0L;
+        }
+    }
+}

+ 41 - 0
flow-common/flow-common-websocket-starter/pom.xml

@@ -0,0 +1,41 @@
+<?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>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-common</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>flow-common-websocket-starter</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-core</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 126 - 0
flow-common/flow-common-websocket-starter/src/main/java/com/flow/common/websocket/configure/WebSocketConfigure.java

@@ -0,0 +1,126 @@
+package com.flow.common.websocket.configure;
+
+import com.flow.common.websocket.core.ClientInboundChannelInterceptor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.messaging.simp.config.ChannelRegistration;
+import org.springframework.messaging.simp.config.MessageBrokerRegistry;
+import org.springframework.messaging.support.ChannelInterceptor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.web.socket.CloseStatus;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
+import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
+import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
+import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
+import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
+
+import java.security.Principal;
+
+@Slf4j
+@Configuration
+@EnableWebSocketMessageBroker
+public class WebSocketConfigure implements WebSocketMessageBrokerConfigurer {
+
+    /**
+     * 这个Bean会自动注册使用@ServerEndpoint注解声明的websocket endpoint
+     * @return
+     */
+    /*@Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }*/
+
+    @Override
+    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
+        // 限制入站消息大小:8KB
+        registry.setMessageSizeLimit(8 * 1024);
+        registry.addDecoratorFactory(new WebSocketHandlerDecoratorFactory(){
+            @Override
+            public WebSocketHandler decorate(WebSocketHandler handler) {
+                return new WebSocketHandlerDecorator(handler){
+                    /**
+                     * 连接建立后
+                     * @param session
+                     * @throws Exception
+                     */
+                    @Override
+                    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
+                        Principal principal = session.getPrincipal();
+                        System.out.println("principal = " + principal);
+                        super.afterConnectionEstablished(session);
+                    }
+
+                    /**
+                     * 连接关闭后
+                     * @param session
+                     * @param closeStatus
+                     * @throws Exception
+                     */
+                    @Override
+                    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
+                        Principal principal = session.getPrincipal();
+                        System.out.println("principal = " + principal);
+                        super.afterConnectionClosed(session, closeStatus);
+                    }
+                };
+            }
+        });
+    }
+
+    @Override
+    public void registerStompEndpoints(StompEndpointRegistry registry) {
+        registry
+                // 注册webSocket端点地址
+                .addEndpoint("/socket")
+                // 可以跨域
+                .setAllowedOrigins("*")
+                // 支持sockJs
+                .withSockJS();
+    }
+
+    /**
+     * 注册相关的消息频道
+     *
+     * @param registry
+     */
+    @Override
+    public void configureMessageBroker(MessageBrokerRegistry registry) {
+        ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
+        te.setPoolSize(1);
+        te.setThreadNamePrefix("wss-heartbeat-thread-");
+        te.initialize();
+        registry
+                // 客户端向服务端发送消息需有/app 前缀
+                // .setApplicationDestinationPrefixes("/app")
+                // 设置客户端接收点对点消息地址的前缀,默认为 /user
+                .setUserDestinationPrefix("/user")
+                // 设置两个频道,topic用于广播,queue用于点对点发送
+                .enableSimpleBroker("/topic", "/queue")
+                // 服务端10s发送一次心跳给客户端,服务端15s未收到心跳则认为连接断开
+                .setHeartbeatValue(new long[]{10000, 0}).setTaskScheduler(te);
+    }
+
+
+    /**
+     * 加入拦截器主要是为了验证权限的
+     *
+     * @param registration
+     */
+    @Override
+    public void configureClientInboundChannel(ChannelRegistration registration) {
+        // 添加通道拦截器
+        registration.interceptors(clientInboundChannelInterceptor());
+    }
+
+    /**
+     * @return stomp入站通道拦截器
+     */
+    @Bean
+    public ChannelInterceptor clientInboundChannelInterceptor() {
+        return new ClientInboundChannelInterceptor();
+    }
+}

+ 102 - 0
flow-common/flow-common-websocket-starter/src/main/java/com/flow/common/websocket/core/ClientInboundChannelInterceptor.java

@@ -0,0 +1,102 @@
+package com.flow.common.websocket.core;
+
+import com.flow.common.core.util.ApplicationContextUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.simp.stomp.StompCommand;
+import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
+import org.springframework.messaging.support.ChannelInterceptor;
+import org.springframework.messaging.support.MessageHeaderAccessor;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.util.StringUtils;
+
+import java.util.Objects;
+
+
+/**
+ * 客户端入站通道拦截器
+ */
+@Order(Ordered.HIGHEST_PRECEDENCE + 99)
+@Slf4j
+public class ClientInboundChannelInterceptor implements ChannelInterceptor {
+    /**
+     * 实际消息发送频道之前调用
+     *
+     * @param message
+     * @param channel
+     * @return
+     */
+    @Override
+    public Message<?> preSend(Message<?> message, MessageChannel channel) {
+        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
+        if (Objects.nonNull(accessor)) {
+            StompPrincipal user = (StompPrincipal) accessor.getUser();
+            if (Objects.nonNull(user)) {
+                OAuth2Authentication auth2Authentication = ApplicationContextUtil.getBean(TokenStore.class).readAuthentication(user.getToken());
+                SecurityContextHolder.getContext().setAuthentication(auth2Authentication);
+            }
+            if (Objects.nonNull(accessor.getCommand())) {
+                if (accessor.getCommand() == StompCommand.CONNECT) {
+                    log.info("【preSend】首次连接");
+                    String authorization = accessor.getFirstNativeHeader("Authorization");
+                    if (!StringUtils.isEmpty(authorization)) {
+                        try {
+                            String token = authorization.replace("Bearer ", "");
+                            OAuth2Authentication oAuth2Authentication = ApplicationContextUtil.getBean(TokenStore.class).readAuthentication(token);
+                            SecurityContextHolder.getContext().setAuthentication(oAuth2Authentication);
+                            UserDetails userDetails = (UserDetails) oAuth2Authentication.getPrincipal();
+                            // 设置当前访问器的认证用户
+                            StompPrincipal principal = new StompPrincipal();
+                            principal.setToken(token);
+                            principal.setName(userDetails.getUsername());
+                            accessor.setUser(principal);
+                        } catch (Exception e) {
+                            throw new IllegalArgumentException("认证失败,不允许连接!");
+                        }
+                    } else {
+                        throw new IllegalArgumentException("认证失败,不允许连接!");
+                    }
+                } else if (accessor.getCommand() == StompCommand.DISCONNECT) {
+                    log.info("【preSend】断开连接");
+                } else if (accessor.getCommand() == StompCommand.SUBSCRIBE) {
+                    log.info("【preSend】订阅成功");
+                } else if (accessor.getCommand() == StompCommand.SEND) {
+                    log.info("【preSend】发送成功");
+                } else if (accessor.getCommand() == StompCommand.ERROR) {
+                    log.info("【preSend】发送失败");
+                }
+            }
+        }
+        return message;
+    }
+
+    /**
+     * 在消息发送后立刻调用,boolean值参数表示该调用的返回值
+     *
+     * @param message
+     * @param channel
+     * @param sent
+     */
+    @Override
+    public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
+        StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
+        if (accessor.getCommand() == StompCommand.CONNECT) {
+            log.info("【postSend】连接成功");
+        } else if (accessor.getCommand() == StompCommand.DISCONNECT) {
+            log.info("【postSend】断开连接");
+        } else if (accessor.getCommand() == StompCommand.SUBSCRIBE) {
+            log.info("【postSend】订阅成功");
+        } else if (accessor.getCommand() == StompCommand.SEND) {
+            log.info("【postSend】发送成功");
+        } else if (accessor.getCommand() == StompCommand.ERROR) {
+            log.info("【postSend】发送失败");
+        }
+        ChannelInterceptor.super.postSend(message, channel, sent);
+    }
+}

+ 26 - 0
flow-common/flow-common-websocket-starter/src/main/java/com/flow/common/websocket/core/StompPrincipal.java

@@ -0,0 +1,26 @@
+package com.flow.common.websocket.core;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.security.Principal;
+
+/**
+ * stomp用户信息
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class StompPrincipal implements Principal {
+    /**
+     * token
+     */
+    private String token;
+    /**
+     * 用户账号
+     */
+    private String name;
+
+}

+ 31 - 0
flow-common/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">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+    <parent>
+        <artifactId>lowflow</artifactId>
+        <groupId>com.flow</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modules>
+        <module>flow-common-core</module>
+        <module>flow-common-redis-starter</module>
+        <module>flow-common-doc-starter</module>
+        <module>flow-common-websocket-starter</module>
+        <module>flow-common-mybatis-starter</module>
+        <module>flow-common-cache-starter</module>
+        <module>flow-common-oauth2-starter</module>
+        <module>flow-common-flowable-starter</module>
+    </modules>
+
+    <artifactId>flow-common</artifactId>
+    <name>common  公共模块</name>
+    <description>公共依赖设置处理</description>
+
+
+    <dependencies>
+
+    </dependencies>
+</project>

+ 29 - 0
flow-file/flow-file-api/pom.xml

@@ -0,0 +1,29 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-file</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-file-api</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-file-entity</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 20 - 0
flow-file/flow-file-api/src/main/java/com/flow/service/FileService.java

@@ -0,0 +1,20 @@
+package com.flow.service;
+
+import com.flow.common.core.model.PageResult;
+import com.flow.common.mybatis.service.BaseService;
+import com.flow.entity.StorageFile;
+import com.flow.model.StorageFileQuery;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Set;
+
+public interface FileService extends BaseService<StorageFile> {
+
+    PageResult<StorageFile> getList(StorageFileQuery storageFileQuery);
+
+    StorageFile upload(MultipartFile file) throws IOException;
+
+    void delete(Set<Long> ids);
+
+}

+ 29 - 0
flow-file/flow-file-biz/pom.xml

@@ -0,0 +1,29 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-file</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-file-biz</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-file-api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 59 - 0
flow-file/flow-file-biz/src/main/java/com/flow/config/WebAppConfigurer.java

@@ -0,0 +1,59 @@
+package com.flow.config;
+
+import com.flow.constant.FileConstant;
+import org.springframework.boot.web.servlet.MultipartConfigFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.unit.DataSize;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import javax.servlet.MultipartConfigElement;
+
+@Configuration
+public class WebAppConfigurer implements WebMvcConfigurer {
+    /**
+     * 静态资源配置
+     *
+     * @param registry
+     */
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        String path = FileConstant.getPath();
+        registry.addResourceHandler("/static/file/**").addResourceLocations("file:" + path).setCachePeriod(0);
+        WebMvcConfigurer.super.addResourceHandlers(registry);
+    }
+
+    @Override
+    public void addCorsMappings(CorsRegistry registry) {
+        registry
+                // 所有接口
+                .addMapping("/**")
+                // 允许跨域请求的域名
+                .allowedOrigins("*")
+                // 是否发送 Cookie
+                .allowCredentials(true)
+                // 所有请求方法
+                .allowedMethods("*")
+                // 允许的 Header
+                .allowedHeaders("*")
+                // 允许的响应 Header
+                .exposedHeaders("*")
+                .maxAge(3600);
+    }
+
+
+    /**
+     * 文件上传配置
+     *
+     * @return
+     */
+    @Bean
+    public MultipartConfigElement multipartConfigElement() {
+        MultipartConfigFactory factory = new MultipartConfigFactory();
+        factory.setMaxFileSize(DataSize.ofBytes(50 * 1024 * 1024));
+        factory.setMaxRequestSize(DataSize.ofBytes(1024 * 1024 * 1024));
+        return factory.createMultipartConfig();
+    }
+}

+ 85 - 0
flow-file/flow-file-biz/src/main/java/com/flow/constant/FileConstant.java

@@ -0,0 +1,85 @@
+package com.flow.constant;
+
+import java.text.DecimalFormat;
+
+public interface FileConstant {
+    /**
+     * 文件大小限制10M
+     */
+    Long MAX_SIZE = 1024 * 1024 * 10L;
+    /**
+     * windows文件路径
+     */
+    String WINDOWS = "D:/flow/file/";
+    /**
+     * linux文件路径
+     */
+    String LINUX = "/home/flow/file/";
+    /**
+     * mac文件路径
+     */
+    String MAC = "/Users/flow/file/";
+
+    /**
+     * 文件上传路径
+     *
+     * @return
+     */
+    static String getPath() {
+        String os = System.getProperty("os.name");
+        if (os.toLowerCase().startsWith("win")) {
+            return WINDOWS;
+        } else if (os.toLowerCase().startsWith("mac")) {
+            return MAC;
+        }
+        return LINUX;
+    }
+
+    /**
+     * 文件大小转换
+     */
+    static String getSize(long size) {
+        int KB = 1024;
+        int GB = 1024 * 1024 * 1024;
+        int MB = 1024 * 1024;
+        DecimalFormat DF = new DecimalFormat("0.00");
+        String resultSize;
+        if (size / GB >= 1) {
+            //如果当前Byte的值大于等于1GB
+            resultSize = DF.format(size / (float) GB) + "GB   ";
+        } else if (size / MB >= 1) {
+            //如果当前Byte的值大于等于1MB
+            resultSize = DF.format(size / (float) MB) + "MB   ";
+        } else if (size / KB >= 1) {
+            //如果当前Byte的值大于等于1KB
+            resultSize = DF.format(size / (float) KB) + "KB   ";
+        } else {
+            resultSize = size + "B   ";
+        }
+        return resultSize;
+    }
+
+    static String getFileType(String type) {
+        String IMAGE = "图片";
+        String TXT = "文档";
+        String MUSIC = "音乐";
+        String VIDEO = "视频";
+        String OTHER = "其他";
+        String documents = "txt doc pdf ppt pps xlsx xls docx";
+        String music = "mp3 wav wma mpa ram ra aac aif m4a";
+        String video = "avi mpg mpe mpeg asf wmv mov qt rm mp4 flv m4v webm ogv ogg";
+        String image = "bmp dib pcp dif wmf gif jpg tif eps psd cdr iff tga pcd mpt png jpeg";
+        if (image.contains(type)) {
+            return IMAGE;
+        } else if (documents.contains(type)) {
+            return TXT;
+        } else if (music.contains(type)) {
+            return MUSIC;
+        } else if (video.contains(type)) {
+            return VIDEO;
+        } else {
+            return OTHER;
+        }
+    }
+
+}

+ 8 - 0
flow-file/flow-file-biz/src/main/java/com/flow/dao/FileDao.java

@@ -0,0 +1,8 @@
+package com.flow.dao;
+
+
+import com.flow.common.mybatis.dao.BaseDao;
+import com.flow.entity.StorageFile;
+
+public interface FileDao extends BaseDao<StorageFile> {
+}

+ 110 - 0
flow-file/flow-file-biz/src/main/java/com/flow/service/impl/FileServiceImpl.java

@@ -0,0 +1,110 @@
+package com.flow.service.impl;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.flow.common.core.exception.BaseException;
+import com.flow.common.core.model.PageResult;
+import com.flow.common.mybatis.service.impl.BaseServiceImpl;
+import com.flow.constant.FileConstant;
+import com.flow.dao.FileDao;
+import com.flow.entity.StorageFile;
+import com.flow.enums.CategoryEnum;
+import com.flow.mapstruct.StorageFileMapper;
+import com.flow.model.StorageFileQuery;
+import com.flow.service.FileService;
+import com.flow.utils.FileUtil;
+import com.google.common.collect.Lists;
+import org.apache.commons.codec.digest.DigestUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+@Service
+public class FileServiceImpl extends BaseServiceImpl<FileDao, StorageFile> implements FileService {
+    @Autowired
+    private FileDao fileDao;
+    @Autowired
+    private StorageFileMapper storageFileMapper;
+
+    @Override
+    public PageResult<StorageFile> getList(StorageFileQuery storageFileQuery) {
+        StorageFile storageFile = storageFileMapper.toEntity(storageFileQuery);
+        Page<StorageFile> page = fileDao.lambdaQueryChain()
+                .setEntity(storageFile)
+                .page(
+                        new Page<>(storageFileQuery.getPage(), storageFileQuery.getLimit())
+                );
+        return new PageResult<>(page.getTotal(), page.getRecords());
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public StorageFile upload(MultipartFile multipartFile) throws IOException {
+        Long maxSize = FileConstant.MAX_SIZE;
+        long fileSize = multipartFile.getSize();
+        if (fileSize > maxSize) {
+            throw new BaseException("文件超出规定大小:" + maxSize + "MB");
+        }
+        String filename = multipartFile.getOriginalFilename();
+        String suffix = FileUtil.getSuffix(filename);
+        File file = FileUtil.upload(multipartFile, FileConstant.getPath());
+        String md5 = DigestUtils.md5Hex(Files.newInputStream(file.toPath()));
+        ArrayList<String> doc = Lists.newArrayList("txt", "pdf", "doc", "docx", "ppt", "pptx", "xlsx", "xls");
+        ArrayList<String> image = Lists.newArrayList("mpt", "pcd", "tga", "iff", "cdr", "psd", "eps", "tif", "wmf", "dif", "pcp", "dib", "jpg", "png", "gif", "jpeg", "bmp");
+        ArrayList<String> video = Lists.newArrayList("flv", "dat", "m4v", "asx", "mov", "mp4", "avi", "rmvb", "rm", "asf", "divx", "mpg", "mpeg", "mpe", "wmv", "mkv", "vob");
+        ArrayList<String> audio = Lists.newArrayList("mp3", "wma", "wav", "mid", "midi", "vqf", "amr", "aac", "flac", "ape", "ogg", "m4a", "ra", "3gp", "mp2");
+        CategoryEnum category = CategoryEnum.OTHER;
+        if (doc.contains(suffix.toLowerCase())) {
+            category = CategoryEnum.DOC;
+        } else if (image.contains(suffix.toLowerCase())) {
+            category = CategoryEnum.IMAGE;
+        } else if (video.contains(suffix.toLowerCase())) {
+            category = CategoryEnum.VIDEO;
+        } else if (audio.contains(suffix.toLowerCase())) {
+            category = CategoryEnum.AUDIO;
+        }
+        String filePath = file.getPath();
+        StorageFile storageFile = new StorageFile(
+                filename,
+                file.getName(),
+                filePath,
+                "/static/file/" + file.getName(),
+                suffix,
+                category,
+                md5,
+                fileSize,
+                null,
+                null
+
+        );
+        fileDao.insert(storageFile);
+        return storageFile;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void delete(Set<Long> ids) {
+        for (Long id : ids) {
+            StorageFile storageFile = fileDao.selectById(id);
+            if (Objects.nonNull(storageFile)) {
+                List<StorageFile> list = fileDao.lambdaQueryChain()
+                        .select(StorageFile::getId)
+                        .eq(StorageFile::getMd5, storageFile.getMd5())
+                        .list();
+                if (list.size() <= 1) {
+                    FileUtil.del(storageFile.getPath());
+                }
+                fileDao.deleteById(id);
+            }
+        }
+
+    }
+}

+ 52 - 0
flow-file/flow-file-biz/src/main/java/com/flow/utils/FileUtil.java

@@ -0,0 +1,52 @@
+package com.flow.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.util.UUID;
+
+public class FileUtil {
+    private static final Logger log = LoggerFactory.getLogger(FileUtil.class);
+
+
+    public static File upload(MultipartFile file, String filePath) {
+        // 日期字符串
+        String format = UUID.randomUUID().toString();
+        // 文件名称
+        String fileName = file.getOriginalFilename();
+        try {
+            // 路径
+            String path = filePath +format +fileName;
+            File dest = new File(path).getCanonicalFile();
+            // 检测是否存在目录
+            if (!dest.getParentFile().exists()) {
+                // 创建目录
+                if (!dest.getParentFile().mkdirs()) {
+                    System.out.println("was not successful.");
+                }
+            }
+            // 文件写入
+            file.transferTo(dest);
+            return dest;
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return null;
+    }
+
+    public static void del(String filePath){
+        if (filePath != null && filePath.length() > 0) {
+            File file = new File(filePath);
+            if (file.exists()) {
+                file.delete();
+            }
+        }
+    }
+
+    public static String getSuffix(String fileName) {
+        return fileName.substring(fileName.lastIndexOf("."));
+    }
+
+}

+ 28 - 0
flow-file/flow-file-controller/pom.xml

@@ -0,0 +1,28 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-file</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-file-controller</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-file-biz</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+</project>

+ 30 - 0
flow-file/flow-file-controller/src/main/java/com/flow/FileController.java

@@ -0,0 +1,30 @@
+package com.flow;
+
+import com.flow.common.core.model.Result;
+import com.flow.entity.StorageFile;
+import com.flow.service.FileService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.IOException;
+import java.util.Set;
+
+@RestController
+@RequestMapping("/file")
+public class FileController {
+    @Autowired
+    private FileService fileService;
+
+    @PostMapping("/upload")
+    public Result<StorageFile> upload(@RequestParam MultipartFile file) throws IOException {
+        return Result.success(fileService.upload(file));
+    }
+
+    // 删除文件
+    @DeleteMapping
+    public Result<?> delete(@RequestBody Set<Long> ids) {
+        fileService.delete(ids);
+        return Result.success();
+    }
+}

+ 21 - 0
flow-file/flow-file-entity/pom.xml

@@ -0,0 +1,21 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-file</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-file-entity</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+</project>

+ 31 - 0
flow-file/flow-file-entity/src/main/java/com/flow/entity/StorageFile.java

@@ -0,0 +1,31 @@
+package com.flow.entity;
+
+import com.baomidou.mybatisplus.annotation.SqlCondition;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.flow.common.mybatis.entity.BaseEntity;
+import com.flow.enums.CategoryEnum;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@TableName(value = "sys_file")
+@EqualsAndHashCode(callSuper = true)
+@AllArgsConstructor
+@Data
+public class StorageFile extends BaseEntity {
+    @TableField(condition = SqlCondition.LIKE)
+    private String name;
+    @TableField(condition = SqlCondition.LIKE)
+    private String realName;
+    private String path;
+    private String url;
+    @TableField(condition = SqlCondition.LIKE)
+    private String type;
+    private CategoryEnum category;
+    private String md5;
+    private Long size;
+    private Long businessId;
+    private Long businessExtraId;
+
+}

+ 30 - 0
flow-file/flow-file-entity/src/main/java/com/flow/enums/CategoryEnum.java

@@ -0,0 +1,30 @@
+package com.flow.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+
+public enum CategoryEnum {
+    IMAGE("image", "图片"),
+    VIDEO("video", "视频"),
+    AUDIO("audio", "音频"),
+    DOC("doc", "文档"),
+    OTHER("other", "其他");
+
+    @EnumValue
+    private String code;
+    @JsonValue
+    private String desc;
+
+    CategoryEnum(String code, String desc) {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+}

+ 12 - 0
flow-file/flow-file-entity/src/main/java/com/flow/mapstruct/StorageFileMapper.java

@@ -0,0 +1,12 @@
+package com.flow.mapstruct;
+
+import com.flow.common.core.model.BaseMapper;
+import com.flow.entity.StorageFile;
+import com.flow.model.StorageFileQuery;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface StorageFileMapper extends BaseMapper<StorageFileQuery, StorageFile> {
+
+}

+ 11 - 0
flow-file/flow-file-entity/src/main/java/com/flow/model/StorageFileQuery.java

@@ -0,0 +1,11 @@
+package com.flow.model;
+
+import com.flow.common.core.model.Query;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class StorageFileQuery extends Query {
+    private String name;
+}

+ 43 - 0
flow-file/pom.xml

@@ -0,0 +1,43 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>lowflow</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <modules>
+        <module>flow-file-api</module>
+        <module>flow-file-biz</module>
+        <module>flow-file-entity</module>
+        <module>flow-file-controller</module>
+    </modules>
+
+    <artifactId>flow-file</artifactId>
+    <name>file  文件模块</name>
+    <description>文件依赖设置处理</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-core</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-mybatis-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 29 - 0
flow-im/flow-im-api/pom.xml

@@ -0,0 +1,29 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-im</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-im-api</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-im-entity</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 7 - 0
flow-im/flow-im-api/src/main/java/com/flow/service/NotificationStrategy.java

@@ -0,0 +1,7 @@
+package com.flow.service;
+
+public interface NotificationStrategy {
+    void send(String receiver, String subject, String content);
+
+    String getType();
+}

+ 26 - 0
flow-im/flow-im-api/src/main/java/com/flow/service/NotifyService.java

@@ -0,0 +1,26 @@
+package com.flow.service;
+
+import com.flow.common.core.model.CursorResult;
+import com.flow.common.core.model.PageResult;
+import com.flow.common.mybatis.service.BaseService;
+import com.flow.entity.Notify;
+import com.flow.model.NotifyQuery;
+
+import java.util.List;
+
+public interface NotifyService extends BaseService<Notify> {
+
+    Notify notify(Notify notify);
+
+    PageResult<Notify> getList(NotifyQuery query);
+
+    CursorResult<Notify> cursor(NotifyQuery query);
+
+    void read(List<Long> ids);
+
+    void readAll();
+
+    void delete(List<Long> ids);
+
+    Long getUnreadCount();
+}

+ 28 - 0
flow-im/flow-im-biz/pom.xml

@@ -0,0 +1,28 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-im</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-im-biz</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-im-api</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+</project>

+ 12 - 0
flow-im/flow-im-biz/src/main/java/com/flow/dao/NotifyDao.java

@@ -0,0 +1,12 @@
+package com.flow.dao;
+
+import com.flow.common.mybatis.dao.BaseDao;
+import com.flow.entity.Notify;
+import com.flow.model.NotifyQuery;
+
+import java.util.List;
+
+public interface NotifyDao extends BaseDao<Notify> {
+
+    List<Notify> cursor(NotifyQuery query);
+}

+ 32 - 0
flow-im/flow-im-biz/src/main/java/com/flow/service/impl/EmailNotificationStrategy.java

@@ -0,0 +1,32 @@
+package com.flow.service.impl;
+
+import com.flow.service.NotificationStrategy;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EmailNotificationStrategy implements NotificationStrategy {
+    @Autowired
+    private JavaMailSender mailSender;
+    @Value("${spring.mail.username}")
+    private String from;
+
+    @Override
+    public void send(String receiver, String subject, String content) {
+        SimpleMailMessage message = new SimpleMailMessage();
+        message.setFrom(this.from);
+        message.setTo(receiver);
+        message.setSubject(subject);
+        message.setText(content);
+        mailSender.send(message);
+    }
+
+
+    @Override
+    public String getType() {
+        return "email";
+    }
+}

+ 43 - 0
flow-im/flow-im-biz/src/main/java/com/flow/service/impl/InAppNotificationStrategy.java

@@ -0,0 +1,43 @@
+package com.flow.service.impl;
+
+import com.flow.entity.MessageEntity;
+import com.flow.entity.Notify;
+import com.flow.service.NotificationStrategy;
+import com.flow.service.NotifyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Service
+public class InAppNotificationStrategy implements NotificationStrategy {
+    @Autowired
+    private SimpMessagingTemplate messagingTemplate;
+    @Autowired
+    private NotifyService notifyService;
+
+    @Override
+    public void send(String receiver, String subject, String content) {
+        Notify notify = new Notify();
+        notify.setReceiver(receiver);
+        notify.setSubject(subject);
+        notify.setContent(content);
+        notify.setReceivingTime(LocalDateTime.now());
+        notify.setSender("admin");
+        boolean save = notifyService.save(notify);
+        if (save) {
+            MessageEntity<Notify> messageEntity = new MessageEntity<>(
+                    notify.getSender(),
+                    notify.getReceiver(),
+                    notify
+            );
+            messagingTemplate.convertAndSendToUser(notify.getReceiver(), "/topic/notify", messageEntity);
+        }
+    }
+
+    @Override
+    public String getType() {
+        return "in_app";
+    }
+}

+ 31 - 0
flow-im/flow-im-biz/src/main/java/com/flow/service/impl/NotificationStrategyFactory.java

@@ -0,0 +1,31 @@
+package com.flow.service.impl;
+
+import com.flow.service.NotificationStrategy;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class NotificationStrategyFactory {
+
+    @Autowired
+    private List<NotificationStrategy> notificationStrategies;
+
+    public <T> T getNotificationStrategy(Class<T> clazz) {
+        for (NotificationStrategy notificationStrategy : notificationStrategies) {
+            if (clazz.isInstance(notificationStrategy)) {
+                return clazz.cast(notificationStrategy);
+            }
+        }
+        return null;
+    }
+
+    public List<NotificationStrategy> getNotificationStrategy(Set<String> types) {
+        return notificationStrategies.stream()
+                .filter(strategy -> types.contains(strategy.getType()))
+                .collect(Collectors.toList());
+    }
+}

+ 100 - 0
flow-im/flow-im-biz/src/main/java/com/flow/service/impl/NotifyServiceImpl.java

@@ -0,0 +1,100 @@
+package com.flow.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.flow.common.core.model.CursorResult;
+import com.flow.common.core.model.PageResult;
+import com.flow.common.mybatis.service.impl.BaseServiceImpl;
+import com.flow.common.oauth2.utils.SecurityContextUtil;
+import com.flow.dao.NotifyDao;
+import com.flow.entity.MessageEntity;
+import com.flow.entity.Notify;
+import com.flow.mapstruct.NotifyMapper;
+import com.flow.model.NotifyQuery;
+import com.flow.service.NotifyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessagingTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class NotifyServiceImpl extends BaseServiceImpl<NotifyDao, Notify> implements NotifyService {
+    @Autowired
+    private NotifyDao notifyDao;
+    @Autowired
+    private NotifyMapper notifyMapper;
+    @Autowired
+    private SimpMessagingTemplate messagingTemplate;
+
+    @Override
+    public Notify notify(Notify notify) {
+        int insert = notifyDao.insert(notify);
+        if (insert > 0) {
+            MessageEntity<Notify> messageEntity = new MessageEntity<>(
+                    notify.getSender(),
+                    notify.getReceiver(),
+                    notify
+            );
+            messagingTemplate.convertAndSendToUser(notify.getReceiver(), "/topic/notify", messageEntity);
+        }
+        return notify;
+    }
+
+    @Override
+    public PageResult<Notify> getList(NotifyQuery query) {
+        query.setReceiver(SecurityContextUtil.getUserId());
+        Notify notify = notifyMapper.toEntity(query);
+        Page<Notify> page = notifyDao.lambdaQueryChain()
+                .setEntity(notify)
+                .orderByDesc(Notify::getId)
+                .page(new Page<>(query.getPage(), query.getLimit()));
+        return new PageResult<>(page.getTotal(), page.getRecords());
+    }
+
+    @Override
+    public CursorResult<Notify> cursor(NotifyQuery query) {
+        query.setReceiver(SecurityContextUtil.getUserId());
+        List<Notify> list = notifyDao.cursor(query);
+        CursorResult<Notify> cursorResult = new CursorResult<>();
+        cursorResult.setRows(list);
+        if (CollectionUtils.isNotEmpty(list)) {
+            cursorResult.setCursor(list.get(list.size() - 1).getId());
+        }
+        return cursorResult;
+    }
+
+    @Override
+    public void read(List<Long> ids) {
+        notifyDao.lambdaUpdateChain()
+                .in(Notify::getId, ids)
+                .set(Notify::getIsRead, true)
+                .update();
+    }
+
+    @Override
+    public void readAll() {
+        String userId = SecurityContextUtil.getUserId();
+        notifyDao.lambdaUpdateChain()
+                .eq(Notify::getReceiver, userId)
+                .set(Notify::getIsRead, true)
+                .update();
+    }
+
+    @Override
+    public void delete(List<Long> ids) {
+        notifyDao.lambdaUpdateChain()
+                .in(Notify::getId, ids)
+                .remove();
+    }
+
+    @Override
+    public Long getUnreadCount() {
+        String userId = SecurityContextUtil.getUserId();
+        return notifyDao.lambdaQueryChain()
+                .select(Notify::getId)
+                .eq(Notify::getReceiver, userId)
+                .eq(Notify::getIsRead, false)
+                .count();
+    }
+}

+ 28 - 0
flow-im/flow-im-controller/pom.xml

@@ -0,0 +1,28 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-im</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>flow-im-controller</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-im-biz</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

+ 20 - 0
flow-im/flow-im-controller/src/main/java/com/flow/config/ImConverterConfig.java

@@ -0,0 +1,20 @@
+package com.flow.config;
+
+import com.flow.enums.NotifyEnum;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.convert.converter.Converter;
+
+@Configuration
+public class ImConverterConfig {
+
+    @Bean
+    public Converter<String, NotifyEnum> notifyEnumConverter(){
+        return new Converter<String, NotifyEnum>() {
+            @Override
+            public NotifyEnum convert(String s) {
+                return NotifyEnum.match(s);
+            }
+        };
+    }
+}

+ 56 - 0
flow-im/flow-im-controller/src/main/java/com/flow/controller/NotifyController.java

@@ -0,0 +1,56 @@
+package com.flow.controller;
+
+import com.flow.common.core.model.CursorResult;
+import com.flow.common.core.model.PageResult;
+import com.flow.common.core.model.Result;
+import com.flow.entity.Notify;
+import com.flow.model.NotifyQuery;
+import com.flow.service.NotifyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.simp.SimpMessageSendingOperations;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/notify")
+public class NotifyController {
+    @Autowired
+    private NotifyService notifyService;
+    @Autowired
+    private SimpMessageSendingOperations simpMessageSendingOperations;
+
+    @GetMapping("/unread")
+    public Result<Long> getUnreadCount() {
+        return Result.success(notifyService.getUnreadCount());
+    }
+
+    @GetMapping
+    public Result<PageResult<Notify>> getList(NotifyQuery notifyQuery) {
+        return Result.success(notifyService.getList(notifyQuery));
+    }
+
+    @GetMapping("/cursor")
+    public Result<CursorResult<Notify>> cursor(NotifyQuery notifyQuery) {
+        return Result.success(notifyService.cursor(notifyQuery));
+    }
+
+    @PutMapping("/read")
+    public Result<?> read(@RequestBody List<Long> ids) {
+        notifyService.read(ids);
+        return Result.success();
+    }
+
+    @PutMapping("/readAll")
+    public Result<?> readAll() {
+        notifyService.readAll();
+        return Result.success();
+    }
+
+    @DeleteMapping
+    public Result<?> delete(@RequestBody List<Long> ids) {
+        notifyService.delete(ids);
+        return Result.success();
+    }
+
+}

+ 21 - 0
flow-im/flow-im-entity/pom.xml

@@ -0,0 +1,21 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-im</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-im-entity</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+</project>

+ 20 - 0
flow-im/flow-im-entity/src/main/java/com/flow/entity/MessageEntity.java

@@ -0,0 +1,20 @@
+package com.flow.entity;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class MessageEntity<T> {
+    private String from;
+    private String to;
+    private T body;
+    private LocalDateTime time;
+
+    public MessageEntity(String from, String to, T body) {
+        this.from = from;
+        this.to = to;
+        this.body = body;
+        this.time = LocalDateTime.now();
+    }
+}

+ 32 - 0
flow-im/flow-im-entity/src/main/java/com/flow/entity/Notify.java

@@ -0,0 +1,32 @@
+package com.flow.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import com.flow.enums.NotifyEnum;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.Map;
+
+@Data
+@TableName(value = "sys_notify", autoResultMap = true)
+public class Notify {
+    @JsonSerialize(using = ToStringSerializer.class)
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    @TableField(condition = SqlCondition.LIKE)
+    private String subject;
+    @TableField(condition = SqlCondition.LIKE)
+    private String content;
+    private String sender;
+    private String receiver;
+    private NotifyEnum type;
+    private LocalDateTime receivingTime;
+    private Boolean isRead;
+    private Boolean archived;
+    private String url;
+    @TableField(typeHandler = JacksonTypeHandler.class)
+    private Map<String, Object> params;
+}

+ 26 - 0
flow-im/flow-im-entity/src/main/java/com/flow/entity/ToEmail.java

@@ -0,0 +1,26 @@
+package com.flow.entity;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ToEmail implements Serializable {
+    /**
+     * 邮件接收方,可多人
+     */
+    private List<String> tos;
+    /**
+     * 邮件主题
+     */
+    private String subject;
+    /**
+     * 邮件内容
+     */
+    private String content;
+}

+ 28 - 0
flow-im/flow-im-entity/src/main/java/com/flow/enums/NotifyEnum.java

@@ -0,0 +1,28 @@
+package com.flow.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@Getter
+@AllArgsConstructor
+public enum NotifyEnum {
+    SYSTEM("system", "系统通知"),
+    TODO("todo", "待办"),
+    MESSAGE("message", "消息");
+
+    @EnumValue
+    @JsonValue
+    private final String type;
+    private final String desc;
+
+    public static NotifyEnum match(String type) {
+        for (NotifyEnum value : NotifyEnum.values()) {
+            if (value.type.equals(type)) {
+                return value;
+            }
+        }
+        return null;
+    }
+}

+ 12 - 0
flow-im/flow-im-entity/src/main/java/com/flow/mapstruct/NotifyMapper.java

@@ -0,0 +1,12 @@
+package com.flow.mapstruct;
+
+import com.flow.common.core.model.BaseMapper;
+import com.flow.entity.Notify;
+import com.flow.model.NotifyQuery;
+import org.mapstruct.Mapper;
+import org.mapstruct.ReportingPolicy;
+
+@Mapper(componentModel = "spring",unmappedTargetPolicy = ReportingPolicy.IGNORE)
+public interface NotifyMapper extends BaseMapper<NotifyQuery, Notify> {
+
+}

+ 17 - 0
flow-im/flow-im-entity/src/main/java/com/flow/model/NotifyQuery.java

@@ -0,0 +1,17 @@
+package com.flow.model;
+
+import com.flow.common.core.model.Query;
+import com.flow.enums.NotifyEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class NotifyQuery extends Query {
+    private String cursorId;
+    private String receiver;
+    private String subject;
+    private String content;
+    private Boolean isRead;
+    private NotifyEnum type;
+}

+ 55 - 0
flow-im/pom.xml

@@ -0,0 +1,55 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>pom</packaging>
+
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>lowflow</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+
+    <modules>
+        <module>flow-im-entity</module>
+        <module>flow-im-api</module>
+        <module>flow-im-biz</module>
+        <module>flow-im-controller</module>
+    </modules>
+
+    <artifactId>flow-im</artifactId>
+    <name>im 消息模块</name>
+    <description>系统依赖设置处理</description>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-core</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-mybatis-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--websocket-->
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-common-websocket-starter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <!--mail-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 29 - 0
flow-oauth/flow-oauth-api/pom.xml

@@ -0,0 +1,29 @@
+<?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">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.flow</groupId>
+        <artifactId>flow-oauth</artifactId>
+        <version>0.0.1-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>flow-oauth-api</artifactId>
+
+    <properties>
+        <maven.compiler.source>8</maven.compiler.source>
+        <maven.compiler.target>8</maven.compiler.target>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.flow</groupId>
+            <artifactId>flow-oauth-entity</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+    </dependencies>
+
+</project>

Some files were not shown because too many files changed in this diff