PC-202203141647\Administrator 3 年 前
コミット
8d1ead1c62
共有100 個のファイルを変更した6555 個の追加0 個の削除を含む
  1. 21 0
      dietary-nutrition-background/.editorconfig
  2. 27 0
      dietary-nutrition-background/.gitignore
  3. 34 0
      dietary-nutrition-background/LICENSE
  4. 43 0
      dietary-nutrition-background/README.md
  5. 19 0
      dietary-nutrition-background/blade-auth/Dockerfile
  6. 32 0
      dietary-nutrition-background/blade-auth/README.md
  7. 177 0
      dietary-nutrition-background/blade-auth/pom.xml
  8. 38 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/AuthApplication.java
  9. 117 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/BladeAuthorizationServerConfiguration.java
  10. 67 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/BladeResourceServerConfiguration.java
  11. 66 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/JwtTokenStoreConfiguration.java
  12. 56 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/SecurityConfiguration.java
  13. 53 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/constant/AuthConstant.java
  14. 88 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/endpoint/BladeSocialEndpoint.java
  15. 96 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/endpoint/BladeTokenEndPoint.java
  16. 45 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/enums/BladeUserEnum.java
  17. 52 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/granter/BladeTokenGranter.java
  18. 86 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java
  19. 128 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java
  20. 82 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/handler/AppLoginInSuccessHandler.java
  21. 48 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/service/BladeClientDetailsServiceImpl.java
  22. 93 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java
  23. 125 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java
  24. 78 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java
  25. 50 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladeNoOpPasswordEncoder.java
  26. 39 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoder.java
  27. 66 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoderFactories.java
  28. 182 0
      dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java
  29. 15 0
      dietary-nutrition-background/blade-auth/src/main/resources/application-dev.yml
  30. 15 0
      dietary-nutrition-background/blade-auth/src/main/resources/application-prod.yml
  31. 15 0
      dietary-nutrition-background/blade-auth/src/main/resources/application-test.yml
  32. 50 0
      dietary-nutrition-background/blade-auth/src/main/resources/application.yml
  33. 55 0
      dietary-nutrition-background/blade-common/pom.xml
  34. 33 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/cache/CacheNames.java
  35. 32 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java
  36. 79 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java
  37. 239 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java
  38. 71 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/constant/TenantConstant.java
  39. 61 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java
  40. 358 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java
  41. 17 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/CustomizeColumnWidth.java
  42. 16 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/DropDownSetField.java
  43. 8 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/DropDownSetImpl.java
  44. 5 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/DropDownSetInterface.java
  45. 142 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/HttpClient.java
  46. 48 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/ProductCellWriteHandler.java
  47. 38 0
      dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/ResoveDropAnnotationUtil.java
  48. 8 0
      dietary-nutrition-background/blade-common/src/main/resources/banner.txt
  49. 15 0
      dietary-nutrition-background/blade-gateway/Dockerfile
  50. 134 0
      dietary-nutrition-background/blade-gateway/pom.xml
  51. 39 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java
  52. 86 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java
  53. 103 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java
  54. 101 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java
  55. 94 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java
  56. 41 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java
  57. 41 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java
  58. 57 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java
  59. 115 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java
  60. 112 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalRequestLogFilter.java
  61. 99 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalResponseLogFilter.java
  62. 63 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java
  63. 101 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java
  64. 55 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/SwaggerResourceHandler.java
  65. 52 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/SwaggerSecurityHandler.java
  66. 52 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/SwaggerUiHandler.java
  67. 41 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java
  68. 38 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/props/RouteProperties.java
  69. 45 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/props/RouteResource.java
  70. 78 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java
  71. 49 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java
  72. 84 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java
  73. 59 0
      dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/SwaggerProvider.java
  74. 10 0
      dietary-nutrition-background/blade-gateway/src/main/resources/application-dev.yml
  75. 12 0
      dietary-nutrition-background/blade-gateway/src/main/resources/bootstrap.yml
  76. 19 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/pom.xml
  77. 61 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/constant/ProcessConstant.java
  78. 179 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/BladeFlow.java
  79. 43 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/FlowEntity.java
  80. 45 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/enums/FlowModeEnum.java
  81. 100 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClient.java
  82. 58 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClientFallback.java
  83. 66 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/FlowUtil.java
  84. 71 0
      dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/TaskUtil.java
  85. 28 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/pom.xml
  86. 71 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Attach.java
  87. 87 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Oss.java
  88. 82 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Sms.java
  89. 62 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/enums/SmsCodeEnum.java
  90. 73 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClient.java
  91. 43 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClientFallback.java
  92. 113 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/utils/SmsUtil.java
  93. 35 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/AttachVO.java
  94. 29 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/OssVO.java
  95. 45 0
      dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/SmsVO.java
  96. 52 0
      dietary-nutrition-background/blade-ops-api/pom.xml
  97. 17 0
      dietary-nutrition-background/blade-ops/blade-admin/DINGDING_README.md
  98. 15 0
      dietary-nutrition-background/blade-ops/blade-admin/Dockerfile
  99. 72 0
      dietary-nutrition-background/blade-ops/blade-admin/pom.xml
  100. 0 0
      dietary-nutrition-background/blade-ops/blade-admin/src/main/java/org/springblade/admin/AdminApplication.java

+ 21 - 0
dietary-nutrition-background/.editorconfig

@@ -0,0 +1,21 @@
+# http://editorconfig.org
+root = true
+
+# 空格替代Tab缩进在各种编辑工具下效果一致
+[*]
+indent_style = space
+indent_size = 4
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.java]
+indent_style = tab
+
+[*.{json,yml}]
+indent_size = 2
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 27 - 0
dietary-nutrition-background/.gitignore

@@ -0,0 +1,27 @@
+# maven #
+target
+
+logs
+
+# windows #
+Thumbs.db
+
+# Mac #
+.DS_Store
+
+# eclipse #
+.settings
+.project
+.classpath
+.log
+*.class
+
+# idea #
+.idea
+*.iml
+
+# Package Files #
+*.jar
+*.war
+*.ear
+/target

ファイルの差分が大きいため隠しています
+ 34 - 0
dietary-nutrition-background/LICENSE


+ 43 - 0
dietary-nutrition-background/README.md

@@ -0,0 +1,43 @@
+## 版权声明
+* BladeX是一个商业化软件,系列产品知识产权归**上海布雷德网络科技**独立所有
+* 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款
+* 更多详情请看:[BladeX商业授权许可协议](/LICENSE)
+
+## 答疑流程
+>1. 遇到问题或Bug
+>2. 业务型问题打断点调试尝试找出问题所在
+>3. 系统型问题通过百度、谷歌、社区查找解决方案
+>4. 未解决问题则进入技术社区进行发帖提问:[https://sns.bladex.vip/](https://sns.bladex.vip/)
+>5. 将帖子地址发至商业群,特别简单三言两语就能描述清楚的也可在答疑时间内发至商业群提问
+>6. 发帖的时候一定要描述清楚,详细描述遇到问题的**重现步骤**、**报错详细信息**、**相关代码与逻辑**、**使用软件版本**以及**操作系统版本**,否则随意发帖提问将会提高我们的答疑难度。
+
+## 答疑时间
+* 工作日:9:00 ~ 17:00 提供答疑,周末、节假日休息,暂停答疑
+* 请勿**私聊提问**,以免被其他用户的消息覆盖从而无法获得答疑
+* 答疑时间外遇到问题可以将问题发帖至[技术社区](https://sns.bladex.vip/),我们后续会逐个回复
+
+## 授权范围
+* 专业版:只可用于**个人学习**及**个人私活**项目,不可用于公司或团队,不可泄露给任何第三方
+* 企业版:可用于**企业名下**的任何项目,企业版员工在**未购买**专业版授权前,只授权开发**所在授权企业名下**的项目,**不得将BladeX用于个人私活**
+* 共同遵守:若甲方需要您提供项目源码,则需代为甲方购买BladeX企业授权,甲方购买后续的所有项目都无需再次购买授权
+
+## 商用权益
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,将BladeX系列产品用于授权范围内的商用项目,并上线运营
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,不限制项目数,不限制服务器数
+* ✔️ 遵守[商业协议](/LICENSE)的前提下,将自行编写的业务代码申请软件著作权
+
+## 何为侵权
+* ❌ 不遵守商业协议,私自销售商业源码
+* ❌ 以任何理由将BladeX源码用于申请软件著作权
+* ❌ 将商业源码以任何途径任何理由泄露给未授权的单位或个人
+* ❌ 开发完毕项目,没有为甲方购买企业授权,向甲方提供了BladeX代码
+* ❌ 基于BladeX拓展研发与BladeX有竞争关系的衍生框架,并将其开源或销售
+
+## 侵权后果
+* 情节较轻:第一次发现警告处理
+* 情节较重:封禁账号,踢出商业群,并保留追究法律责任的权利
+* 情节严重:与本地律师事务所合作,以公司名义起诉侵犯计算机软件著作权
+
+## 举报有奖
+* 向官方提供有用线索并成功捣毁盗版个人或窝点,将会看成果给予 500~10000 不等的现金奖励
+* 官方唯一指定QQ:1272154962

+ 19 - 0
dietary-nutrition-background/blade-auth/Dockerfile

@@ -0,0 +1,19 @@
+FROM adoptopenjdk/openjdk8-openj9:alpine-slim
+
+MAINTAINER smallchill@163.com
+
+#验证码字体包
+RUN set -xe \
+&& apk --no-cache add ttf-dejavu fontconfig
+
+RUN mkdir -p /blade/auth
+
+WORKDIR /blade/auth
+
+EXPOSE 8100
+
+ADD ./target/blade-auth.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 32 - 0
dietary-nutrition-background/blade-auth/README.md

@@ -0,0 +1,32 @@
+## 目前主要支持的oauth协议
+一、 授权码模式
+授权码模式(authorization_code)主要针对第三方应用,是最为复杂也最为安全的一种模式,操作步骤如下
+1. 访问地址:http://localhost:8100/oauth/authorize?client_id=blade&redirect_uri=http://example.com&code=233333&response_type=code
+2. 获取跳转后的code值(http://example.com/?code=VhYNLR)之后,调用 http://localhost/blade-auth/oauth/token 传入对应的参数
+
+请求头:
+Authorization : Basic YmxhZGU6YmxhZGU= ("YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码)
+
+表单:
+grant_type:authorization_code
+scope:all
+code:VhYNLR
+redirect_uri: http://example.com
+
+二、 密码模式
+密码模式(password)主要针对自家应用,可信度较高,所以可以使用简便安全共存的模式,操作步骤如下
+1. 直接调用 http://localhost/blade-auth/oauth/token 传入对应的参数
+
+请求头:
+Authorization : Basic YmxhZGU6YmxhZGU= ("YmxhZGU6YmxhZGU="为clientId:clientSecret串转换为的base64编码)
+
+表单:
+grant_type:password
+scope:all
+username:admin
+password:123456
+
+## 获取到token后如何获取用户信息
+1. 拼接请求头
+Authorization :bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTU1MzE2MTA5NSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6IjE0YmMyYjAyLTgxY2UtNDFiNC04ZTI3LTA5YWE0ZmU4ZWMwYyIsImNsaWVudF9pZCI6ImJsYWRlIn0.jTmioQDq-fSNNn7YCwl3wP0JE-etSWtzLDe545mDbP4
+2. 调用 http://localhost/blade-auth/oauth/user-info 既可获得对应用户信息

+ 177 - 0
dietary-nutrition-background/blade-auth/pom.xml

@@ -0,0 +1,177 @@
+<?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>
+        <artifactId>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+
+    <artifactId>blade-auth</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springblade</groupId>
+                    <artifactId>blade-scope-api</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-db</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-cloud</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-redis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-swagger</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-social</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>${bladex.project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-system-api</artifactId>
+            <version>${bladex.project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-security</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security.oauth</groupId>
+            <artifactId>spring-security-oauth2</artifactId>
+            <version>2.3.5.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+            <version>2.2.0.RELEASE</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.security</groupId>
+            <artifactId>spring-security-jwt</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+        <!-- 验证码 -->
+        <dependency>
+            <groupId>com.github.whvcse</groupId>
+            <artifactId>easy-captcha</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>2.6.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>2.6.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>2.6.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>2.6.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>2.6.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-user-api</artifactId>
+            <version>2.6.0.RELEASE</version>
+            <scope>compile</scope>
+        </dependency>
+        <!-- 链路追踪、服务监控 -->
+        <!--<dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-trace</artifactId>
+        </dependency>-->
+        <!-- 解决Java11无法运行的问题 -->
+        <!--<dependency>
+            <groupId>javax.xml.bind</groupId>
+            <artifactId>jaxb-api</artifactId>
+            <version>2.2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-core</artifactId>
+            <version>2.2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>com.sun.xml.bind</groupId>
+            <artifactId>jaxb-impl</artifactId>
+            <version>2.2.11</version>
+        </dependency>
+        <dependency>
+            <groupId>javax.activation</groupId>
+            <artifactId>activation</artifactId>
+            <version>1.1.1</version>
+        </dependency>-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>dockerfile-maven-plugin</artifactId>
+                <configuration>
+                    <username>${docker.username}</username>
+                    <password>${docker.password}</password>
+                    <repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
+                    <tag>${project.version}</tag>
+                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                    <buildArgs>
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                    </buildArgs>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 38 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/AuthApplication.java

@@ -0,0 +1,38 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth;
+
+
+import org.springblade.core.cloud.feign.EnableBladeFeign;
+import org.springblade.core.launch.BladeApplication;
+import org.springblade.core.launch.constant.AppConstant;
+import org.springframework.cloud.client.SpringCloudApplication;
+
+/**
+ * 用户认证服务器
+ *
+ * @author Chill
+ */
+@EnableBladeFeign
+@SpringCloudApplication
+public class AuthApplication {
+
+	public static void main(String[] args) {
+		BladeApplication.run(AppConstant.APPLICATION_AUTH_NAME, AuthApplication.class, args);
+	}
+
+}

+ 117 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/BladeAuthorizationServerConfiguration.java

@@ -0,0 +1,117 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.granter.BladeTokenGranter;
+import org.springblade.auth.service.BladeClientDetailsServiceImpl;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.core.userdetails.UserDetailsService;
+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.TokenGranter;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+
+import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 认证服务器配置
+ *
+ * @author Chill
+ */
+@Order
+@Configuration
+@AllArgsConstructor
+@EnableAuthorizationServer
+public class BladeAuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
+
+	private final DataSource dataSource;
+
+	private final AuthenticationManager authenticationManager;
+
+	private final UserDetailsService userDetailsService;
+
+	private final TokenStore tokenStore;
+
+	private final TokenEnhancer jwtTokenEnhancer;
+
+	private final JwtAccessTokenConverter jwtAccessTokenConverter;
+
+	private final BladeRedis bladeRedis;
+
+	private final IUserClient userClient;
+
+	private final SocialProperties socialProperties;
+
+	@Override
+	public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
+		//获取自定义tokenGranter
+		TokenGranter tokenGranter = BladeTokenGranter.getTokenGranter(authenticationManager, endpoints, bladeRedis, userClient, socialProperties);
+
+		//配置端点
+		endpoints.tokenStore(tokenStore)
+			.authenticationManager(authenticationManager)
+			.userDetailsService(userDetailsService)
+			.tokenGranter(tokenGranter);
+
+		//扩展token返回结果
+		if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) {
+			TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
+			List<TokenEnhancer> enhancerList = new ArrayList<>();
+			enhancerList.add(jwtTokenEnhancer);
+			enhancerList.add(jwtAccessTokenConverter);
+			tokenEnhancerChain.setTokenEnhancers(enhancerList);
+			//jwt增强
+			endpoints.tokenEnhancer(tokenEnhancerChain).accessTokenConverter(jwtAccessTokenConverter);
+		}
+	}
+
+	/**
+	 * 配置客户端信息
+	 */
+	@Override
+	@SneakyThrows
+	public void configure(ClientDetailsServiceConfigurer clients) {
+		BladeClientDetailsServiceImpl clientDetailsService = new BladeClientDetailsServiceImpl(dataSource);
+		clientDetailsService.setSelectClientDetailsSql(AuthConstant.DEFAULT_SELECT_STATEMENT);
+		clientDetailsService.setFindClientDetailsSql(AuthConstant.DEFAULT_FIND_STATEMENT);
+		clients.withClientDetails(clientDetailsService);
+	}
+
+	@Override
+	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
+		oauthServer
+			.allowFormAuthenticationForClients()
+			.tokenKeyAccess("permitAll()")
+			.checkTokenAccess("isAuthenticated()");
+	}
+}

+ 67 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/BladeResourceServerConfiguration.java

@@ -0,0 +1,67 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+
+/**
+ * 自定义登录成功配置
+ *
+ * @author Chill
+ */
+@Configuration
+@AllArgsConstructor
+@EnableResourceServer
+public class BladeResourceServerConfiguration extends ResourceServerConfigurerAdapter {
+
+	/**
+	 * 自定义登录成功处理器
+	 */
+	private final AuthenticationSuccessHandler appLoginInSuccessHandler;
+
+	@Override
+	@SneakyThrows
+	public void configure(HttpSecurity http) {
+		http.headers().frameOptions().disable();
+		http.formLogin()
+			.successHandler(appLoginInSuccessHandler)
+			.and()
+			.authorizeRequests()
+			.antMatchers(
+				"/actuator/**",
+				"/oauth/captcha",
+				"/oauth/logout",
+				"/oauth/clear-cache",
+				"/oauth/render/**",
+				"/oauth/callback/**",
+				"/oauth/revoke/**",
+				"/oauth/refresh/**",
+				"/token/**",
+				"/mobile/**",
+				"/v2/api-docs",
+				"/v2/api-docs-ext").permitAll()
+			.anyRequest().authenticated().and()
+			.csrf().disable();
+	}
+
+}

+ 66 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/JwtTokenStoreConfiguration.java

@@ -0,0 +1,66 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import org.springblade.auth.support.BladeJwtTokenEnhancer;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
+
+/**
+ * JwtTokenStore
+ *
+ * @author Chill
+ */
+@Configuration
+@ConditionalOnProperty(prefix = "blade.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true)
+public class JwtTokenStoreConfiguration {
+
+	/**
+	 * 使用jwtTokenStore存储token
+	 */
+	@Bean
+	public TokenStore jwtTokenStore(JwtProperties jwtProperties) {
+		return new JwtTokenStore(jwtAccessTokenConverter(jwtProperties));
+	}
+
+	/**
+	 * 用于生成jwt
+	 */
+	@Bean
+	public JwtAccessTokenConverter jwtAccessTokenConverter(JwtProperties jwtProperties) {
+		JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
+		accessTokenConverter.setSigningKey(jwtProperties.getSignKey());
+		return accessTokenConverter;
+	}
+
+	/**
+	 * 用于扩展jwt
+	 */
+	@Bean
+	@ConditionalOnMissingBean(name = "jwtTokenEnhancer")
+	public TokenEnhancer jwtTokenEnhancer(JwtAccessTokenConverter jwtAccessTokenConverter, JwtProperties jwtProperties) {
+		return new BladeJwtTokenEnhancer(jwtAccessTokenConverter, jwtProperties);
+	}
+
+}

+ 56 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/config/SecurityConfiguration.java

@@ -0,0 +1,56 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.config;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.auth.support.BladePasswordEncoderFactories;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * Security配置
+ *
+ * @author Chill
+ */
+@Configuration
+@AllArgsConstructor
+public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
+
+	@Bean
+	@Override
+	@SneakyThrows
+	public AuthenticationManager authenticationManagerBean() {
+		return super.authenticationManagerBean();
+	}
+
+	@Bean
+	public PasswordEncoder passwordEncoder() {
+		return BladePasswordEncoderFactories.createDelegatingPasswordEncoder();
+	}
+
+	@Override
+	@SneakyThrows
+	protected void configure(HttpSecurity http) {
+		http.httpBasic().and().csrf().disable();
+	}
+
+}

+ 53 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/constant/AuthConstant.java

@@ -0,0 +1,53 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.constant;
+
+/**
+ * 授权校验常量
+ *
+ * @author Chill
+ */
+public interface AuthConstant {
+
+	/**
+	 * 密码加密规则
+	 */
+	String ENCRYPT = "{blade}";
+
+	/**
+	 * blade_client表字段
+	 */
+	String CLIENT_FIELDS = "client_id, CONCAT('{noop}',client_secret) as client_secret, resource_ids, scope, authorized_grant_types, " +
+		"web_server_redirect_uri, authorities, access_token_validity, " +
+		"refresh_token_validity, additional_information, autoapprove";
+
+	/**
+	 * blade_client查询语句
+	 */
+	String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from blade_client";
+
+	/**
+	 * blade_client查询排序
+	 */
+	String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id";
+
+	/**
+	 * 查询client_id
+	 */
+	String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id = ?";
+
+}

+ 88 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/endpoint/BladeSocialEndpoint.java

@@ -0,0 +1,88 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.endpoint;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthToken;
+import me.zhyd.oauth.request.AuthRequest;
+import me.zhyd.oauth.utils.AuthStateUtils;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.core.social.utils.SocialUtil;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * SocialEndpoint
+ *
+ * @author Chill
+ */
+@NonDS
+@Slf4j
+@RestController
+@AllArgsConstructor
+@ConditionalOnProperty(value = "social.enabled", havingValue = "true")
+public class BladeSocialEndpoint {
+
+	private final SocialProperties socialProperties;
+
+	/**
+	 * 授权完毕跳转
+	 */
+	@RequestMapping("/oauth/render/{source}")
+	public void renderAuth(@PathVariable("source") String source, HttpServletResponse response) throws IOException {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
+		response.sendRedirect(authorizeUrl);
+	}
+
+	/**
+	 * 获取认证信息
+	 */
+	@RequestMapping("/oauth/callback/{source}")
+	public Object login(@PathVariable("source") String source, AuthCallback callback) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.login(callback);
+	}
+
+	/**
+	 * 撤销授权
+	 */
+	@RequestMapping("/oauth/revoke/{source}/{token}")
+	public Object revokeAuth(@PathVariable("source") String source, @PathVariable("token") String token) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.revoke(AuthToken.builder().accessToken(token).build());
+	}
+
+	/**
+	 * 续期令牌
+	 */
+	@RequestMapping("/oauth/refresh/{source}")
+	public Object refreshAuth(@PathVariable("source") String source, String token) {
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		return authRequest.refresh(AuthToken.builder().refreshToken(token).build());
+	}
+
+
+}

+ 96 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/endpoint/BladeTokenEndPoint.java

@@ -0,0 +1,96 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.endpoint;
+
+import com.wf.captcha.SpecCaptcha;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.core.cache.constant.CacheConstant;
+import org.springblade.core.cache.utils.CacheUtil;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.secure.BladeUser;
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tenant.annotation.NonDS;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.support.Kv;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.time.Duration;
+
+/**
+ * BladeEndPoint
+ *
+ * @author Chill
+ */
+@NonDS
+@Slf4j
+@RestController
+@AllArgsConstructor
+public class BladeTokenEndPoint {
+
+	private final BladeRedis bladeRedis;
+	private final JwtProperties jwtProperties;
+
+	@GetMapping("/oauth/user-info")
+	public R<Authentication> currentUser(Authentication authentication) {
+		return R.data(authentication);
+	}
+
+	@GetMapping("/oauth/captcha")
+	public Kv captcha() {
+		SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 5);
+		String verCode = specCaptcha.text().toLowerCase();
+		String key = StringUtil.randomUUID();
+		// 存入redis并设置过期时间为30分钟
+		bladeRedis.setEx(CacheNames.CAPTCHA_KEY + key, verCode, Duration.ofMinutes(30));
+		// 将key和base64返回给前端
+		return Kv.create().set("key", key).set("image", specCaptcha.toBase64());
+	}
+
+	@GetMapping("/oauth/logout")
+	public Kv logout() {
+		BladeUser user = AuthUtil.getUser();
+		if (user != null && jwtProperties.getState()) {
+			String token = JwtUtil.getToken(WebUtil.getRequest().getHeader(TokenConstant.HEADER));
+			JwtUtil.removeAccessToken(user.getTenantId(), String.valueOf(user.getUserId()), token);
+		}
+		return Kv.create().set("success", "true").set("msg", "success");
+	}
+
+	@GetMapping("/oauth/clear-cache")
+	public Kv clearCache() {
+		CacheUtil.clear(CacheConstant.BIZ_CACHE);
+		CacheUtil.clear(CacheConstant.USER_CACHE);
+		CacheUtil.clear(CacheConstant.DICT_CACHE);
+		CacheUtil.clear(CacheConstant.FLOW_CACHE);
+		CacheUtil.clear(CacheConstant.SYS_CACHE);
+		CacheUtil.clear(CacheConstant.PARAM_CACHE);
+		CacheUtil.clear(CacheConstant.RESOURCE_CACHE);
+		CacheUtil.clear(CacheConstant.MENU_CACHE);
+		CacheUtil.clear(CacheConstant.MENU_CACHE, Boolean.FALSE);
+		return Kv.create().set("success", "true").set("msg", "success");
+	}
+
+}

+ 45 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/enums/BladeUserEnum.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 用户类型枚举
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum BladeUserEnum {
+
+	/**
+	 * web
+	 */
+	WEB("web", 1),
+
+	/**
+	 * app
+	 */
+	APP("app", 2),
+	;
+
+	final String name;
+	final int category;
+
+}

+ 52 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/granter/BladeTokenGranter.java

@@ -0,0 +1,52 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.granter;
+
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.provider.CompositeTokenGranter;
+import org.springframework.security.oauth2.provider.TokenGranter;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 自定义拓展TokenGranter
+ *
+ * @author Chill
+ */
+public class BladeTokenGranter {
+
+	/**
+	 * 自定义tokenGranter
+	 */
+	public static TokenGranter getTokenGranter(final AuthenticationManager authenticationManager, final AuthorizationServerEndpointsConfigurer endpoints, BladeRedis bladeRedis, IUserClient userClient, SocialProperties socialProperties) {
+		// 默认tokenGranter集合
+		List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
+		// 增加验证码模式
+		granters.add(new CaptchaTokenGranter(authenticationManager, endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), bladeRedis));
+		// 增加第三方登陆模式
+		granters.add(new SocialTokenGranter(endpoints.getTokenServices(), endpoints.getClientDetailsService(), endpoints.getOAuth2RequestFactory(), userClient, socialProperties));
+		// 组合tokenGranter集合
+		return new CompositeTokenGranter(granters);
+	}
+
+}

+ 86 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/granter/CaptchaTokenGranter.java

@@ -0,0 +1,86 @@
+package org.springblade.auth.granter;
+
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.common.cache.CacheNames;
+import org.springblade.core.redis.cache.BladeRedis;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springframework.security.authentication.*;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+import org.springframework.security.oauth2.provider.*;
+import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
+import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 验证码TokenGranter
+ *
+ * @author Chill
+ */
+public class CaptchaTokenGranter extends AbstractTokenGranter {
+
+	private static final String GRANT_TYPE = "captcha";
+
+	private final AuthenticationManager authenticationManager;
+
+	private BladeRedis bladeRedis;
+
+	public CaptchaTokenGranter(AuthenticationManager authenticationManager,
+							   AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, BladeRedis bladeRedis) {
+		this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
+		this.bladeRedis = bladeRedis;
+	}
+
+	protected CaptchaTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
+												ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
+		super(tokenServices, clientDetailsService, requestFactory, grantType);
+		this.authenticationManager = authenticationManager;
+	}
+
+	@Override
+	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
+		HttpServletRequest request = WebUtil.getRequest();
+		// 增加验证码判断
+		String key = request.getHeader(TokenUtil.CAPTCHA_HEADER_KEY);
+		String code = request.getHeader(TokenUtil.CAPTCHA_HEADER_CODE);
+		// 获取验证码
+		String redisCode = bladeRedis.get(CacheNames.CAPTCHA_KEY + key);
+		// 判断验证码
+		if(Func.isNotEmpty(code)){
+			if (!StringUtil.equalsIgnoreCase(redisCode, code)) {
+				throw new UserDeniedAuthorizationException(TokenUtil.CAPTCHA_NOT_CORRECT);
+
+			}
+		}
+
+		Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
+		String username = parameters.get("username");
+		String password = parameters.get("password");
+		// Protect from downstream leaks of password
+		parameters.remove("password");
+
+		Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
+		((AbstractAuthenticationToken) userAuth).setDetails(parameters);
+		try {
+			userAuth = authenticationManager.authenticate(userAuth);
+		}
+		catch (AccountStatusException | BadCredentialsException ase) {
+			//covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
+			throw new InvalidGrantException(ase.getMessage());
+		}
+		// If the username/password are wrong the spec says we should send 400/invalid grant
+
+		if (userAuth == null || !userAuth.isAuthenticated()) {
+			throw new InvalidGrantException("Could not authenticate user: " + username);
+		}
+
+		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
+		return new OAuth2Authentication(storedOAuth2Request, userAuth);
+	}
+}

+ 128 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/granter/SocialTokenGranter.java

@@ -0,0 +1,128 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.granter;
+
+import me.zhyd.oauth.model.AuthCallback;
+import me.zhyd.oauth.model.AuthResponse;
+import me.zhyd.oauth.model.AuthUser;
+import me.zhyd.oauth.request.AuthRequest;
+import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.service.BladeUserDetails;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.core.social.props.SocialProperties;
+import org.springblade.core.social.utils.SocialUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.BeanUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.entity.UserInfo;
+import org.springblade.system.user.entity.UserOauth;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.security.authentication.AbstractAuthenticationToken;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
+import org.springframework.security.oauth2.provider.*;
+import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
+import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 第三方登录认证类
+ *
+ * @author Chill
+ */
+public class SocialTokenGranter extends AbstractTokenGranter {
+	private static final String GRANT_TYPE = "social";
+	private static final Integer AUTH_SUCCESS_CODE = 2000;
+
+	private final IUserClient userClient;
+	private final SocialProperties socialProperties;
+
+	protected SocialTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, IUserClient userClient, SocialProperties socialProperties) {
+		super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
+		this.userClient = userClient;
+		this.socialProperties = socialProperties;
+	}
+
+	@Override
+	protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
+		// 请求头租户信息
+		HttpServletRequest request = WebUtil.getRequest();
+		String tenantId = Func.toStr(request.getHeader(TokenUtil.TENANT_HEADER_KEY), TokenUtil.DEFAULT_TENANT_ID);
+
+		Map<String, String> parameters = new LinkedHashMap<>(tokenRequest.getRequestParameters());
+		// 开放平台来源
+		String sourceParameter = parameters.get("source");
+		// 匹配是否有别名定义
+		String source = socialProperties.getAlias().getOrDefault(sourceParameter, sourceParameter);
+		// 开放平台授权码
+		String code = parameters.get("code");
+		// 开放平台状态吗
+		String state = parameters.get("state");
+
+		// 获取开放平台授权数据
+		AuthRequest authRequest = SocialUtil.getAuthRequest(source, socialProperties);
+		AuthCallback authCallback = new AuthCallback();
+		authCallback.setCode(code);
+		authCallback.setState(state);
+		AuthResponse authResponse = authRequest.login(authCallback);
+		AuthUser authUser;
+		if (authResponse.getCode() == AUTH_SUCCESS_CODE) {
+			authUser = (AuthUser) authResponse.getData();
+		} else {
+			throw new InvalidGrantException("social grant failure, auth response is not success");
+		}
+
+		// 组装数据
+		UserOauth userOauth = Objects.requireNonNull(BeanUtil.copy(authUser, UserOauth.class));
+		userOauth.setSource(authUser.getSource());
+		userOauth.setTenantId(tenantId);
+		userOauth.setUuid(authUser.getUuid());
+
+		// 远程调用,获取认证信息
+		R<UserInfo> result = userClient.userAuthInfo(userOauth);
+		BladeUserDetails bladeUserDetails;
+		if (result.isSuccess()) {
+			User user = result.getData().getUser();
+			if (user == null) {
+				throw new InvalidGrantException("social grant failure, user is null");
+			}
+			bladeUserDetails = new BladeUserDetails(user.getId(),
+				tenantId, result.getData().getOauthId(), user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(userOauth.getAvatar(), TokenUtil.DEFAULT_AVATAR),
+				userOauth.getUsername(), AuthConstant.ENCRYPT + user.getPassword(), true, true, true, true,
+				AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
+		} else {
+			throw new InvalidGrantException("social grant failure, feign client return error");
+		}
+
+		// 组装认证数据,关闭密码校验
+		Authentication userAuth = new UsernamePasswordAuthenticationToken(bladeUserDetails, null, bladeUserDetails.getAuthorities());
+		((AbstractAuthenticationToken) userAuth).setDetails(parameters);
+		OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
+
+		// 返回 OAuth2Authentication
+		return new OAuth2Authentication(storedOAuth2Request, userAuth);
+	}
+
+}

+ 82 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/handler/AppLoginInSuccessHandler.java

@@ -0,0 +1,82 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.handler;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
+import org.springframework.security.oauth2.provider.*;
+import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
+import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * APP登录成功处理器
+ *
+ * @author Chill
+ */
+@Slf4j
+@AllArgsConstructor
+@Component("appLoginInSuccessHandler")
+public class AppLoginInSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
+
+	private final PasswordEncoder passwordEncoder;
+
+	private final ClientDetailsService clientDetailsService;
+
+	private final AuthorizationServerTokenServices authorizationServerTokenServices;
+
+	@Override
+	public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException {
+		log.info("【AppLoginInSuccessHandler】 onAuthenticationSuccess authentication={}", authentication);
+
+		String[] tokens = TokenUtil.extractAndDecodeHeader();
+		if (tokens.length != 2) {
+			throw new UnapprovedClientAuthenticationException("client对应的配置信息不存在");
+		}
+		String clientId = tokens[0];
+		String clientSecret = tokens[1];
+
+		ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
+		if (clientDetails == null) {
+			throw new UnapprovedClientAuthenticationException("clientId 对应的配置信息不存在" + clientId);
+		} else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) {
+			throw new UnapprovedClientAuthenticationException("clientSecret 不匹配" + clientId);
+		}
+
+		TokenRequest tokenRequest = new TokenRequest(new HashMap<>(16), clientId, clientDetails.getScope(), "app");
+		OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
+		OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication);
+		OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
+
+		response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
+		response.getWriter().write(JsonUtil.toJson(token));
+	}
+
+}

+ 48 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/service/BladeClientDetailsServiceImpl.java

@@ -0,0 +1,48 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.service;
+
+import lombok.SneakyThrows;
+import org.springframework.security.oauth2.provider.ClientDetails;
+import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
+import org.springframework.stereotype.Component;
+
+import javax.sql.DataSource;
+
+/**
+ * 客户端信息
+ *
+ * @author Chill
+ */
+@Component
+public class BladeClientDetailsServiceImpl extends JdbcClientDetailsService {
+
+	public BladeClientDetailsServiceImpl(DataSource dataSource) {
+		super(dataSource);
+	}
+
+	/**
+	 * 缓存客户端信息
+	 *
+	 * @param clientId 客户端id
+	 */
+	@Override
+	@SneakyThrows
+	public ClientDetails loadClientByClientId(String clientId) {
+		return super.loadClientByClientId(clientId);
+	}
+}

+ 93 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetails.java

@@ -0,0 +1,93 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.service;
+
+import lombok.Getter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.User;
+
+import java.util.Collection;
+
+/**
+ * 用户信息拓展
+ *
+ * @author Chill
+ */
+@Getter
+public class BladeUserDetails extends User {
+
+	/**
+	 * 用户id
+	 */
+	private final Long userId;
+	/**
+	 * 租户ID
+	 */
+	private final String tenantId;
+	/**
+	 * 第三方认证ID
+	 */
+	private final String oauthId;
+	/**
+	 * 昵称
+	 */
+	private final String name;
+	/**
+	 * 真名
+	 */
+	private final String realName;
+	/**
+	 * 账号
+	 */
+	private final String account;
+	/**
+	 * 部门id
+	 */
+	private final String deptId;
+	/**
+	 * 岗位id
+	 */
+	private final String postId;
+	/**
+	 * 角色id
+	 */
+	private final String roleId;
+	/**
+	 * 角色名
+	 */
+	private final String roleName;
+	/**
+	 * 头像
+	 */
+	private final String avatar;
+
+	public BladeUserDetails(Long userId, String tenantId, String oauthId, String name, String realName, String deptId, String postId, String roleId, String roleName, String avatar, String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
+		super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
+		this.userId = userId;
+		this.tenantId = tenantId;
+		this.oauthId = oauthId;
+		this.name = name;
+		this.realName = realName;
+		this.account = username;
+		this.deptId = deptId;
+		this.postId = postId;
+		this.roleId = roleId;
+		this.roleName = roleName;
+		this.avatar = avatar;
+	}
+
+}

+ 125 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/service/BladeUserDetailsServiceImpl.java

@@ -0,0 +1,125 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.service;
+
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import org.springblade.auth.constant.AuthConstant;
+import org.springblade.auth.enums.BladeUserEnum;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringPool;
+import org.springblade.core.tool.utils.WebUtil;
+import org.springblade.system.entity.Tenant;
+import org.springblade.system.feign.ISysClient;
+import org.springblade.system.user.entity.User;
+import org.springblade.system.user.entity.UserInfo;
+import org.springblade.system.user.feign.IUserClient;
+import org.springframework.security.core.authority.AuthorityUtils;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 用户信息
+ *
+ * @author Chill
+ */
+@Service
+@AllArgsConstructor
+public class BladeUserDetailsServiceImpl implements UserDetailsService {
+
+	private final IUserClient userClient;
+	private final ISysClient sysClient;
+
+
+	@Override
+	@SneakyThrows
+	public BladeUserDetails loadUserByUsername(String username) {
+		HttpServletRequest request = WebUtil.getRequest();
+		// 获取租户ID
+		String headerTenant = request.getHeader(TokenUtil.TENANT_HEADER_KEY);
+		String paramTenant = request.getParameter(TokenUtil.TENANT_PARAM_KEY);
+		//取消租户ID的判断
+		/*if (StringUtil.isAllBlank(headerTenant, paramTenant)) {
+			throw new UserDeniedAuthorizationException(TokenUtil.TENANT_NOT_FOUND);
+		}*/
+		//根据username获取tenantid
+
+		User userTemp= userClient.userByAccount(username).getData();
+		if(userTemp==null){
+			throw new UserDeniedAuthorizationException("无法获取到用户");
+		}
+
+
+		String tenantId=userTemp.getTenantId();
+
+		  username=userTemp.getAccount();
+		//String tenantId = StringUtils.isBlank(headerTenant) ? paramTenant : headerTenant;
+
+		// 获取租户信息
+		R<Tenant> tenant = sysClient.getTenant(tenantId);
+		if (tenant.isSuccess()) {
+			if (TokenUtil.judgeTenant(tenant.getData())) {
+				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+			}
+		} else {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
+		}
+
+
+		// 获取用户类型
+		String userType = Func.toStr(request.getHeader(TokenUtil.USER_TYPE_HEADER_KEY), TokenUtil.DEFAULT_USER_TYPE);
+
+		// 远程调用返回数据
+		R<UserInfo> result;
+		// 根据不同用户类型调用对应的接口返回数据,用户可自行拓展
+		if (userType.equals(BladeUserEnum.WEB.getName())) {
+			result = userClient.userInfo(tenantId, username);
+		} else if (userType.equals(BladeUserEnum.APP.getName())) {
+			result = userClient.userInfo(tenantId, username);
+		} else {
+			result = userClient.userInfo(tenantId, username);
+		}
+
+		// 判断返回信息
+		if (result.isSuccess()) {
+			UserInfo userInfo = result.getData();
+			User user = userInfo.getUser();
+			if (user == null || user.getId() == null) {
+				throw new UsernameNotFoundException(TokenUtil.USER_NOT_FOUND);
+			}
+			if (Func.isEmpty(userInfo.getRoles())) {
+				throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_ROLE);
+			}
+
+			sysClient.saveLogin(tenantId);//登录记录保存
+
+			return new BladeUserDetails(user.getId(),
+				user.getTenantId(), StringPool.EMPTY, user.getName(), user.getRealName(), user.getDeptId(), user.getPostId(), user.getRoleId(), Func.join(result.getData().getRoles()), Func.toStr(user.getAvatar(), TokenUtil.DEFAULT_AVATAR),
+				username, AuthConstant.ENCRYPT + user.getPassword(), true, true, true, true,
+				AuthorityUtils.commaSeparatedStringToAuthorityList(Func.join(result.getData().getRoles())));
+		} else {
+			throw new UsernameNotFoundException(result.getMsg());
+		}
+	}
+
+}

+ 78 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladeJwtTokenEnhancer.java

@@ -0,0 +1,78 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.support;
+
+import lombok.AllArgsConstructor;
+import org.springblade.auth.service.BladeUserDetails;
+import org.springblade.auth.utils.TokenUtil;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.tool.utils.Func;
+import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
+import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.provider.OAuth2Authentication;
+import org.springframework.security.oauth2.provider.token.TokenEnhancer;
+import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * jwt返回参数增强
+ *
+ * @author Chill
+ */
+@AllArgsConstructor
+public class BladeJwtTokenEnhancer implements TokenEnhancer {
+
+	private final JwtAccessTokenConverter jwtAccessTokenConverter;
+	private final JwtProperties jwtProperties;
+
+	@Override
+	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
+		BladeUserDetails principal = (BladeUserDetails) authentication.getUserAuthentication().getPrincipal();
+
+		//token参数增强
+		Map<String, Object> info = new HashMap<>(16);
+		info.put(TokenUtil.CLIENT_ID, TokenUtil.getClientIdFromHeader());
+		info.put(TokenUtil.USER_ID, Func.toStr(principal.getUserId()));
+		info.put(TokenUtil.DEPT_ID, Func.toStr(principal.getDeptId()));
+		info.put(TokenUtil.POST_ID, Func.toStr(principal.getPostId()));
+		info.put(TokenUtil.ROLE_ID, Func.toStr(principal.getRoleId()));
+		info.put(TokenUtil.TENANT_ID, principal.getTenantId());
+		info.put(TokenUtil.OAUTH_ID, principal.getOauthId());
+		info.put(TokenUtil.ACCOUNT, principal.getAccount());
+		info.put(TokenUtil.USER_NAME, principal.getUsername());
+		info.put(TokenUtil.NICK_NAME, principal.getName());
+		info.put(TokenUtil.REAL_NAME, principal.getRealName());
+		info.put(TokenUtil.ROLE_NAME, principal.getRoleName());
+		info.put(TokenUtil.AVATAR, principal.getAvatar());
+		info.put(TokenUtil.LICENSE, TokenUtil.LICENSE_NAME);
+		((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info);
+
+		//token状态设置
+		if (jwtProperties.getState()) {
+			OAuth2AccessToken oAuth2AccessToken = jwtAccessTokenConverter.enhance(accessToken, authentication);
+			String tokenValue = oAuth2AccessToken.getValue();
+			String tenantId = principal.getTenantId();
+			String userId = Func.toStr(principal.getUserId());
+			JwtUtil.addAccessToken(tenantId, userId, tokenValue, accessToken.getExpiresIn());
+		}
+
+		return accessToken;
+	}
+}

+ 50 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladeNoOpPasswordEncoder.java

@@ -0,0 +1,50 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.support;
+
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * 无密码加密
+ *
+ * @author Chill
+ */
+public class BladeNoOpPasswordEncoder implements PasswordEncoder {
+
+	@Override
+	public String encode(CharSequence rawPassword) {
+		return rawPassword.toString();
+	}
+
+	@Override
+	public boolean matches(CharSequence rawPassword, String encodedPassword) {
+		return rawPassword.toString().equals(encodedPassword);
+	}
+
+	/**
+	 * Get the singleton {@link BladeNoOpPasswordEncoder}.
+	 */
+	public static PasswordEncoder getInstance() {
+		return INSTANCE;
+	}
+
+	private static final PasswordEncoder INSTANCE = new BladeNoOpPasswordEncoder();
+
+	private BladeNoOpPasswordEncoder() {
+	}
+
+}

+ 39 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoder.java

@@ -0,0 +1,39 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.support;
+
+import org.springblade.core.tool.utils.DigestUtil;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
+/**
+ * 自定义密码加密
+ *
+ * @author Chill
+ */
+public class BladePasswordEncoder implements PasswordEncoder {
+
+	@Override
+	public String encode(CharSequence rawPassword) {
+		return DigestUtil.hex((String) rawPassword);
+	}
+
+	@Override
+	public boolean matches(CharSequence rawPassword, String encodedPassword) {
+		return encodedPassword.equals(encode(rawPassword));
+	}
+
+}

+ 66 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/support/BladePasswordEncoderFactories.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright 2002-2017 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.auth.support;
+
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
+import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 自定义密码工厂
+ *
+ * @author Rob Winch, Chill
+ * @since 5.0
+ */
+public class BladePasswordEncoderFactories {
+
+	/**
+	 * Creates a {@link DelegatingPasswordEncoder} with default mappings. Additional
+	 * mappings may be added and the encoding will be updated to conform with best
+	 * practices. However, due to the nature of {@link DelegatingPasswordEncoder} the
+	 * updates should not impact users. The mappings current are:
+	 *
+	 * <ul>
+	 * <li>blade - {@link BladePasswordEncoder} (sha1(md5("password")))</li>
+	 * <li>bcrypt - {@link BCryptPasswordEncoder} (Also used for encoding)</li>
+	 * <li>noop - {@link BladeNoOpPasswordEncoder}</li>
+	 * <li>pbkdf2 - {@link Pbkdf2PasswordEncoder}</li>
+	 * <li>scrypt - {@link SCryptPasswordEncoder}</li>
+	 * </ul>
+	 *
+	 * @return the {@link PasswordEncoder} to use
+	 */
+	public static PasswordEncoder createDelegatingPasswordEncoder() {
+		String encodingId = "blade";
+		Map<String, PasswordEncoder> encoders = new HashMap<>(16);
+		encoders.put(encodingId, new BladePasswordEncoder());
+		encoders.put("bcrypt", new BCryptPasswordEncoder());
+		encoders.put("noop", BladeNoOpPasswordEncoder.getInstance());
+		encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
+		encoders.put("scrypt", new SCryptPasswordEncoder());
+
+		return new DelegatingPasswordEncoder(encodingId, encoders);
+	}
+
+	private BladePasswordEncoderFactories() {
+	}
+
+}

+ 182 - 0
dietary-nutrition-background/blade-auth/src/main/java/org/springblade/auth/utils/TokenUtil.java

@@ -0,0 +1,182 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.auth.utils;
+
+import lombok.SneakyThrows;
+import org.springblade.common.constant.TenantConstant;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.core.tenant.BladeTenantProperties;
+import org.springblade.core.tool.constant.BladeConstant;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.*;
+import org.springblade.system.entity.Tenant;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
+import org.springframework.security.oauth2.common.exceptions.UserDeniedAuthorizationException;
+
+import java.util.Base64;
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * 认证工具类
+ *
+ * @author Chill
+ */
+public class TokenUtil {
+
+	public final static String AVATAR = TokenConstant.AVATAR;
+	public final static String ACCOUNT = TokenConstant.ACCOUNT;
+	public final static String USER_NAME = TokenConstant.USER_NAME;
+	public final static String NICK_NAME = TokenConstant.NICK_NAME;
+	public final static String REAL_NAME = TokenConstant.REAL_NAME;
+	public final static String USER_ID = TokenConstant.USER_ID;
+	public final static String DEPT_ID = TokenConstant.DEPT_ID;
+	public final static String POST_ID = TokenConstant.POST_ID;
+	public final static String ROLE_ID = TokenConstant.ROLE_ID;
+	public final static String ROLE_NAME = TokenConstant.ROLE_NAME;
+	public final static String TENANT_ID = TokenConstant.TENANT_ID;
+	public final static String OAUTH_ID = TokenConstant.OAUTH_ID;
+	public final static String CLIENT_ID = TokenConstant.CLIENT_ID;
+	public final static String LICENSE = TokenConstant.LICENSE;
+	public final static String LICENSE_NAME = TokenConstant.LICENSE_NAME;
+
+	public final static String CAPTCHA_HEADER_KEY = "Captcha-Key";
+	public final static String CAPTCHA_HEADER_CODE = "Captcha-Code";
+	public final static String CAPTCHA_NOT_CORRECT = "验证码不正确";
+	public final static String TENANT_HEADER_KEY = "Tenant-Id";
+	public final static String TENANT_PARAM_KEY = "tenant_id";
+	public final static String DEFAULT_TENANT_ID = "000000";
+	public final static String TENANT_NOT_FOUND = "租户ID未找到";
+	public final static String USER_TYPE_HEADER_KEY = "User-Type";
+	public final static String DEFAULT_USER_TYPE = "web";
+	public final static String USER_NOT_FOUND = "用户名或密码错误";
+	public final static String USER_HAS_NO_ROLE = "未获得用户的角色信息";
+	public final static String USER_HAS_NO_TENANT = "未获得用户的租户信息";
+	public final static String USER_HAS_NO_TENANT_PERMISSION = "租户授权已过期,请联系管理员";
+	public final static String HEADER_KEY = "Authorization";
+	public final static String HEADER_PREFIX = "Basic ";
+	public final static String DEFAULT_AVATAR = "";
+
+	private static BladeTenantProperties tenantProperties;
+
+	/**
+	 * 获取租户配置
+	 *
+	 * @return tenantProperties
+	 */
+	private static BladeTenantProperties getTenantProperties() {
+		if (tenantProperties == null) {
+			tenantProperties = SpringUtil.getBean(BladeTenantProperties.class);
+		}
+		return tenantProperties;
+	}
+
+	/**
+	 * 解码
+	 */
+	@SneakyThrows
+	public static String[] extractAndDecodeHeader() {
+		String header = WebUtil.getRequest().getHeader(TokenUtil.HEADER_KEY);
+		if (header == null || !header.startsWith(TokenUtil.HEADER_PREFIX)) {
+			throw new UnapprovedClientAuthenticationException("请求头中无client信息");
+		}
+
+		byte[] base64Token = header.substring(6).getBytes(Charsets.UTF_8_NAME);
+
+		byte[] decoded;
+		try {
+			decoded = Base64.getDecoder().decode(base64Token);
+		} catch (IllegalArgumentException var7) {
+			throw new BadCredentialsException("Failed to decode basic authentication token");
+		}
+
+		String token = new String(decoded, Charsets.UTF_8_NAME);
+		int index = token.indexOf(StringPool.COLON);
+		if (index == -1) {
+			throw new BadCredentialsException("Invalid basic authentication token");
+		} else {
+			return new String[]{token.substring(0, index), token.substring(index + 1)};
+		}
+	}
+
+	/**
+	 * 获取请求头中的客户端id
+	 */
+	public static String getClientIdFromHeader() {
+		String[] tokens = extractAndDecodeHeader();
+		return tokens[0];
+	}
+
+	/**
+	 * 获取token过期时间(次日凌晨3点)
+	 *
+	 * @return expire
+	 */
+	public static int getTokenValiditySecond() {
+		Calendar cal = Calendar.getInstance();
+		cal.add(Calendar.DAY_OF_YEAR, 1);
+		cal.set(Calendar.HOUR_OF_DAY, 3);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		return (int) (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000;
+	}
+
+	/**
+	 * 获取refreshToken过期时间
+	 *
+	 * @return expire
+	 */
+	public static int getRefreshTokenValiditySeconds() {
+		return 60 * 60 * 24 * 15;
+	}
+
+	/**
+	 * 判断租户权限
+	 *
+	 * @param tenant 租户信息
+	 * @return boolean
+	 */
+	public static boolean judgeTenant(Tenant tenant) {
+		if (tenant == null) {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT);
+		}
+		if (StringUtil.equalsIgnoreCase(tenant.getTenantId(), BladeConstant.ADMIN_TENANT_ID)) {
+			return false;
+		}
+		Date expireTime = tenant.getExpireTime();
+		Date startTime = tenant.getStartUseTime();
+		if (getTenantProperties().getLicense()) {
+			String licenseKey = tenant.getLicenseKey();
+			String decrypt = DesUtil.decryptFormHex(licenseKey, TenantConstant.DES_KEY);
+			expireTime = JsonUtil.parse(decrypt, Tenant.class).getExpireTime();
+		}
+		if (expireTime != null && expireTime.before(DateUtil.now())) {
+			throw new UserDeniedAuthorizationException(TokenUtil.USER_HAS_NO_TENANT_PERMISSION);
+		}
+
+		if (startTime != null && startTime.after(DateUtil.now())) {
+			throw new UserDeniedAuthorizationException("租户未生效,请确认开始使用日期");
+		}
+		if(tenant.getStatus().intValue()!=0){
+			throw new UserDeniedAuthorizationException("机构已被停用或删除,请联系平台");
+		}
+		return false;
+	}
+
+}

+ 15 - 0
dietary-nutrition-background/blade-auth/src/main/resources/application-dev.yml

@@ -0,0 +1,15 @@
+#服务器端口
+server:
+  port: 8100
+
+#数据源配置
+spring:
+  datasource:
+    url: ${blade.datasource.dev.url}
+    username: ${blade.datasource.dev.username}
+    password: ${blade.datasource.dev.password}
+
+#第三方登陆
+social:
+  enabled: true
+  domain: http://127.0.0.1:1888

+ 15 - 0
dietary-nutrition-background/blade-auth/src/main/resources/application-prod.yml

@@ -0,0 +1,15 @@
+#服务器端口
+server:
+  port: 8100
+
+#数据源配置
+spring:
+  datasource:
+    url: ${blade.datasource.prod.url}
+    username: ${blade.datasource.prod.username}
+    password: ${blade.datasource.prod.password}
+
+#第三方登陆
+social:
+  enabled: true
+  domain: http://127.0.0.1:1888

+ 15 - 0
dietary-nutrition-background/blade-auth/src/main/resources/application-test.yml

@@ -0,0 +1,15 @@
+#服务器端口
+server:
+  port: 8100
+
+#数据源配置
+spring:
+  datasource:
+    url: ${blade.datasource.test.url}
+    username: ${blade.datasource.test.username}
+    password: ${blade.datasource.test.password}
+
+#第三方登陆
+social:
+  enabled: true
+  domain: http://127.0.0.1:1888

+ 50 - 0
dietary-nutrition-background/blade-auth/src/main/resources/application.yml

@@ -0,0 +1,50 @@
+# 在使用Spring默认数据源Hikari的情况下配置以下配置项
+spring:
+  datasource:
+    hikari:
+      # 自动提交从池中返回的连接
+      auto-commit: true
+      # 连接池中维护的最小空闲连接数
+      minimum-idle: 10
+      # 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count)
+      maximum-pool-size: 60
+      # 空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
+      # 只有空闲连接数大于最大连接数且空闲时间超过该值,才会被释放
+      idle-timeout: 30000
+      # 连接最大存活时间.不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
+      max-lifetime: 1800000
+      # 等待连接池分配连接的最大时长(毫秒),超过这个时长还没可用的连接则发生SQLException, 缺省:30秒
+      connection-timeout: 30000
+      # 连接测试查询
+      connection-test-query: select 1
+      #connection-test-query: select 1 from dual
+
+#swagger文档
+swagger:
+  base-packages:
+    - org.springblade
+    - org.springframework.security.oauth2.provider.endpoint
+
+#第三方登陆
+social:
+  oauth:
+    GITHUB:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/github
+    GITEE:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/gitee
+    WECHAT_OPEN:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/wechat
+    QQ:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/qq
+    DINGTALK:
+      client-id: 233************
+      client-secret: 233************************************
+      redirect-uri: ${social.domain}/oauth/redirect/dingtalk

+ 55 - 0
dietary-nutrition-background/blade-common/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">
+    <parent>
+        <artifactId>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-common</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-launch</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>easyexcel</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-httpclient</groupId>
+            <artifactId>commons-httpclient</artifactId>
+            <version>3.0.1</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                    <finalName>${project.name}</finalName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 33 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/cache/CacheNames.java

@@ -0,0 +1,33 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.cache;
+
+/**
+ * 缓存名
+ *
+ * @author Chill
+ */
+public interface CacheNames {
+
+	String CAPTCHA_KEY = "blade:auth::blade:captcha:";
+	String NUTAGE_CACHE="blade:nutage";
+	String FOOD_CACHE="blade:food";
+	//验证码请求
+	String CODE_KEY="blade:code:";
+
+
+}

+ 32 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java

@@ -0,0 +1,32 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.config;
+
+
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 公共封装包配置类
+ *
+ * @author Chill
+ */
+@Configuration
+@AllArgsConstructor
+public class BladeCommonConfiguration {
+
+}

+ 79 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/constant/CommonConstant.java

@@ -0,0 +1,79 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.constant;
+
+/**
+ * 通用常量
+ *
+ * @author Chill
+ */
+public interface CommonConstant {
+
+	/**
+	 * sword 系统名
+	 */
+	String SWORD_NAME = "sword";
+
+	/**
+	 * saber 系统名
+	 */
+	String SABER_NAME = "saber";
+
+	/**
+	 * 顶级父节点id
+	 */
+	Long TOP_PARENT_ID = 0L;
+
+	/**
+	 * 顶级父节点名称
+	 */
+	String TOP_PARENT_NAME = "顶级";
+
+	/**
+	 * 未封存状态值
+	 */
+	Integer NOT_SEALED_ID = 0;
+
+	/**
+	 * 默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 默认密码参数值
+	 */
+	String DEFAULT_PARAM_PASSWORD = "account.initPassword";
+
+	/**
+	 * 默认排序字段
+	 */
+	String SORT_FIELD = "sort";
+
+	/**
+	 * 数据权限类型
+	 */
+	Integer DATA_SCOPE_CATEGORY = 1;
+
+	/**
+	 * 接口权限类型
+	 */
+	Integer API_SCOPE_CATEGORY = 2;
+
+	String SUPPLY_CODE_XIAOZHEN="xiaozhen";
+
+
+}

+ 239 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/constant/LauncherConstant.java

@@ -0,0 +1,239 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.constant;
+
+import org.springblade.core.launch.constant.AppConstant;
+
+import static org.springblade.core.launch.constant.AppConstant.APPLICATION_NAME_PREFIX;
+
+/**
+ * 启动常量
+ *
+ * @author Chill
+ */
+public interface LauncherConstant {
+
+	/**
+	 * xxljob
+	 */
+	String APPLICATION_XXLJOB_NAME = APPLICATION_NAME_PREFIX + "xxljob";
+
+	/**
+	 * xxljob
+	 */
+	String APPLICATION_XXLJOB_ADMIN_NAME = APPLICATION_NAME_PREFIX + "xxljob-admin";
+
+	/**
+	 * nacos dev 地址
+	 */
+	//正式
+//	String NACOS_DEV_ADDR = "127.0.0.1:8848";
+//	膳食测试环境
+	String NACOS_DEV_ADDR = "127.0.0.1:8848";
+//	String NACOS_DEV_ADDR = "192.168.2.41:8848";
+	/**
+	 * nacos prod 地址
+	 */
+//	String NACOS_PROD_ADDR = "172.30.0.48:8848";
+	//膳食正式环境
+	String NACOS_PROD_ADDR = "121.40.96.63:8848";
+
+	/**
+	 * nacos test 地址
+	 */
+	String NACOS_TEST_ADDR = "172.30.0.48:8848";
+
+	/**
+	 * sentinel dev 地址
+	 */
+	String SENTINEL_DEV_ADDR = "127.0.0.1:8858";
+
+	/**
+	 * sentinel prod 地址
+	 */
+	String SENTINEL_PROD_ADDR = "172.30.0.58:8858";
+
+	/**
+	 * sentinel test 地址
+	 */
+	String SENTINEL_TEST_ADDR = "172.30.0.58:8858";
+
+	/**
+	 * seata dev 地址
+	 */
+	String SEATA_DEV_ADDR = "127.0.0.1:8091";
+
+	/**
+	 * seata prod 地址
+	 */
+	String SEATA_PROD_ADDR = "172.30.0.68:8091";
+
+	/**
+	 * seata test 地址
+	 */
+	String SEATA_TEST_ADDR = "172.30.0.68:8091";
+
+	/**
+	 * zipkin dev 地址
+	 */
+	String ZIPKIN_DEV_ADDR = "http://127.0.0.1:9411";
+
+	/**
+	 * zipkin prod 地址
+	 */
+	String ZIPKIN_PROD_ADDR = "http://172.30.0.71:9411";
+
+	/**
+	 * zipkin test 地址
+	 */
+	String ZIPKIN_TEST_ADDR = "http://172.30.0.71:9411";
+
+	/**
+	 * elk dev 地址
+	 */
+	String ELK_DEV_ADDR = "127.0.0.1:9000";
+
+	/**
+	 * elk prod 地址
+	 */
+	String ELK_PROD_ADDR = "172.30.0.72:9000";
+
+	/**
+	 * elk test 地址
+	 */
+	String ELK_TEST_ADDR = "172.30.0.72:9000";
+
+	/**
+	 * seata file模式
+	 */
+	String FILE_MODE = "file";
+
+	/**
+	 * seata nacos模式
+	 */
+	String NACOS_MODE = "nacos";
+
+	/**
+	 * seata default模式
+	 */
+	String DEFAULT_MODE = "default";
+
+	/**
+	 * seata group后缀
+	 */
+	String GROUP_NAME = "-group";
+
+	/**blade-food**/
+	String APPLICATION_FOOD_NAME=APPLICATION_NAME_PREFIX+"food";
+
+
+	/**
+	 * seata 服务组格式
+	 *
+	 * @param appName 服务名
+	 * @return group
+	 */
+	static String seataServiceGroup(String appName) {
+		return appName.concat(GROUP_NAME);
+	}
+
+	/**
+	 * 动态获取nacos地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String nacosAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return NACOS_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return NACOS_TEST_ADDR;
+			default:
+				return NACOS_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取sentinel地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String sentinelAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return SENTINEL_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return SENTINEL_TEST_ADDR;
+			default:
+				return SENTINEL_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取seata地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String seataAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return SEATA_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return SEATA_TEST_ADDR;
+			default:
+				return SEATA_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取zipkin地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String zipkinAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return ZIPKIN_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return ZIPKIN_TEST_ADDR;
+			default:
+				return ZIPKIN_DEV_ADDR;
+		}
+	}
+
+	/**
+	 * 动态获取elk地址
+	 *
+	 * @param profile 环境变量
+	 * @return addr
+	 */
+	static String elkAddr(String profile) {
+		switch (profile) {
+			case (AppConstant.PROD_CODE):
+				return ELK_PROD_ADDR;
+			case (AppConstant.TEST_CODE):
+				return ELK_TEST_ADDR;
+			default:
+				return ELK_DEV_ADDR;
+		}
+	}
+
+}

+ 71 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/constant/TenantConstant.java

@@ -0,0 +1,71 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.constant;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 租户常量
+ *
+ * @author Chill
+ */
+public interface TenantConstant {
+
+	/**
+	 * 租户默认密码KEY
+	 */
+	String PASSWORD_KEY = "tenant.default.password";
+
+	/**
+	 * 租户默认账号额度KEY
+	 */
+	String ACCOUNT_NUMBER_KEY = "tenant.default.accountNumber";
+
+	/**
+	 * 机构租户默认菜单集合KEY
+	 */
+	String JG_ACCOUNT_MENU_CODE_KEY = "JG.tenant.default.menuCode";
+
+	/**
+	 * 政府租户默认菜单集合KEY
+	 */
+	String ZF_ACCOUNT_MENU_CODE_KEY = "ZF.tenant.default.menuCode";
+
+	/**
+	 * 租户默认密码
+	 */
+	String DEFAULT_PASSWORD = "123456";
+
+	/**
+	 * 租户授权码默认16位密钥
+	 */
+	String DES_KEY = "0000000000000000";
+
+	/**
+	 * 租户默认账号额度
+	 */
+	Integer DEFAULT_ACCOUNT_NUMBER = -1;
+
+	/**
+	 * 租户默认菜单集合
+	 */
+	List<String> MENU_CODES = Arrays.asList(
+		"desk", "flow", "work", "monitor", "resource", "role", "user", "dept", "dictbiz", "topmenu"
+	);
+
+}

+ 61 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java

@@ -0,0 +1,61 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.launch;
+
+import org.springblade.common.constant.LauncherConstant;
+import org.springblade.core.auto.service.AutoService;
+import org.springblade.core.launch.service.LauncherService;
+import org.springblade.core.launch.utils.PropsUtil;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+
+import java.util.Properties;
+
+/**
+ * 启动参数拓展
+ *
+ * @author smallchil
+ */
+@AutoService(LauncherService.class)
+public class LauncherServiceImpl implements LauncherService {
+
+	@Override
+	public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) {
+		Properties props = System.getProperties();
+		// 通用注册
+		PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", LauncherConstant.nacosAddr(profile));
+		PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", LauncherConstant.nacosAddr(profile));
+		PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile));
+		PropsUtil.setProperty(props, "spring.zipkin.base-url", LauncherConstant.zipkinAddr(profile));
+		PropsUtil.setProperty(props, "spring.datasource.dynamic.enabled", "false");
+
+		// 开启elk日志
+		// PropsUtil.setProperty(props, "blade.log.elk.destination", LauncherConstant.elkAddr(profile));
+
+		// seata注册地址
+		// PropsUtil.setProperty(props, "seata.service.grouplist.default", LauncherConstant.seataAddr(profile));
+		// seata注册group格式
+		// PropsUtil.setProperty(props, "seata.tx-service-group", LauncherConstant.seataServiceGroup(appName));
+		// seata配置服务group
+		// PropsUtil.setProperty(props, "seata.service.vgroup-mapping.".concat(LauncherConstant.seataServiceGroup(appName)), LauncherConstant.DEFAULT_MODE);
+		// seata注册模式配置
+		// PropsUtil.setProperty(props, "seata.registry.type", LauncherConstant.NACOS_MODE);
+		// PropsUtil.setProperty(props, "seata.registry.nacos.server-addr", LauncherConstant.nacosAddr(profile));
+		// PropsUtil.setProperty(props, "seata.config.type", LauncherConstant.NACOS_MODE);
+		// PropsUtil.setProperty(props, "seata.config.nacos.server-addr", LauncherConstant.nacosAddr(profile));
+	}
+
+}

+ 358 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/CommonUtil.java

@@ -0,0 +1,358 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.common.utils;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.util.Calendar;
+import java.util.UUID;
+
+/**
+ * 通用工具类
+ *
+ * @author Chill
+ */
+public class CommonUtil {
+	private static Logger logger = LoggerFactory.getLogger(CommonUtil.class);
+
+	public   static Integer APPROVED_0=0;//待审核
+	public  static Integer APPROVED_1=1;//审核通过
+	public  static Integer APPROVED_2=2;//审核不通过
+	public  static Integer APPROVED_3=3;//无须审核
+
+	public  static Integer ISPUB_0=0;//公开
+	public  static Integer ISPUB_1=1;//不公开不分享
+	public  static Integer ISPUB_2=2;//不公开要分享
+
+	public  static  Integer ISDELETE_0=0;//正常
+	public  static  Integer ISDELETE_1=1;//删除
+
+	/**obj转字符串**/
+	public  static  String processNull(Object obj){
+		if(obj==null){
+			return "";
+		}else{
+			return String.valueOf(obj);
+		}
+	}
+
+	public static String dealArr(String[] arr){
+		StringBuffer str=new StringBuffer();
+		if(arr!=null && arr.length>0){
+			for (int i = 0; i <arr.length ; i++) {
+				if (i==arr.length-1){
+					str.append(arr[i]);
+				}else {
+					str.append(arr[i]+",");
+				}
+			}
+		}else{
+			return  null;
+		}
+		return str.toString();
+	}
+
+	/**
+	 * 最大公约数
+	 * @param m 数值1
+	 * @param n 数值2
+	 * @return
+	 */
+	public static int getMaxGY(int m,int n){
+		if(m==0){
+			return n;
+		}else if(n==0){
+			return m;
+		}
+		//求最大公约数
+		if(m==n){
+			return n;
+		}else{
+			while(m%n!=0){
+				int temp=m%n;
+				m=n;
+				n=temp;
+			}
+			return n;
+		}
+	}
+
+	public  static Integer TENANT_1=1;//1-平台
+	public  static Integer TENANT_2=2;//2-机构
+	public  static Integer TENANT_3=3;//3-政府
+
+	public  static Integer CLASSTYPE_0=0;//3-机构主账号名称
+	public  static Integer CLASSTYPE_1=1;//1-学段
+	public  static Integer CLASSTYPE_2=2;//2-年级
+	public  static Integer CLASSTYPE_3=3;//3-班级
+
+
+	public static String TYPE_GL="gl";//谷类
+	public static String TYPE_SL="sl";//薯类
+	public static String TYPE_SC="sc";//蔬菜
+	public static String TYPE_SG="sg";//水果
+	public static String TYPE_RZP="rzp";//乳制品
+	public static String TYPE_DD="dd";//大豆
+	public static String TYPE_JG="jg";//坚果
+	public static String TYPE_XQL="xql";//畜禽类
+	public static String TYPE_DL="dl";//蛋类
+	public static String TYPE_SCP="scp";//水产品
+	public static String TYPE_SY="sy";//食盐
+	public static String TYPE_SYY="syy";//食用油
+
+	public static  String BASE_TYPE="gl,sl,sc,sg,rzp,dd,xql,dl,scp,jg,sy,syy,jzl,yye,xcl,ssl,yll,hjj,mjl,twp,qt";
+
+	public static  String BASE_TYPE_NAME="谷类及制品,薯类、淀粉及制品,蔬菜类及制品,水果类及制品,乳类及制品,干豆类及制品,畜禽肉类及制品,蛋类及制品,鱼虾蟹贝类,坚果、种子类,食盐,油脂类,菌藻类,婴幼儿食品,小吃、甜饼,速食食品,饮料类,含酒精饮料,糖、蜜饯类,调味品类,药食两用食物及其他";
+
+	//食盐的固定id
+	/*public static String  SY_IDS="";*/
+
+	//分数统计哪些分类
+	public static  String BASE_TYPE_COUNT="gl,sl,sc,sg,rzp,dd,xql,dl,scp,jg,sy,syy";
+
+
+	//分数统计哪些营养素
+	public static  String TYPE_Nutri="101,102,103,104,201,204,301,303,401,405,406,415";
+
+
+	//带量食谱分类
+	public static  String QUANTITY_BASE_TYPE="gl,sl,sc,sg,rzp,dd,xql,dl,scp,jg,sy,syy,jzl,yye,xcl,ssl,yll,hjj,mjl,twp";
+
+
+	//带量食谱展示哪些营养素
+	public static  String QUANTITY_TYPE_Nutri="101,102,103,104,501,401,402,405,406,412,415,403,504,201,202,203,204,205,301,303,304,305,308,302";
+
+	//营养摄入量分析
+	public static  String NUTRI_TYPE="101,102,103,201,204,301,303,401,405,406,415";
+
+
+	public  String logUpload(String viewpath,MultipartFile file,String uploadpath) throws Exception {
+		if (file == null || file.isEmpty()) {
+			throw new Exception("未选择需上传的文件");
+		}
+
+		//String filePath ="logs_app";
+		//获取项目根路径
+		//String a=	System.getProperty("user.dir");
+		//a=new File(a).getParent();
+
+		//filePath=uploadpath+File.separator+filePath;
+		String filePath=uploadpath;
+		File fileUpload = new File(filePath);
+		if (!fileUpload.exists()) {
+			fileUpload.mkdirs();
+		}
+
+		//获取文件后缀
+		String extName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1);
+		//file.getOriginalFilename()
+		//文件名称
+        String original=UUID.randomUUID()+"."+extName;
+		String savePath =filePath+ File.separator +original;
+		String url=viewpath;
+		try {
+			FileCopyUtils.copy(file.getBytes(),new File(savePath));
+			/*if (savePath.contains("\\")) {
+				savePath = savePath.replace("\\", "/");
+			}*/
+			return url+original;
+		} catch (IOException e) {
+			throw new Exception("上传文件到服务器失败:" + e.toString());
+		}
+	}
+
+
+	public void download(HttpServletRequest request, HttpServletResponse response) {
+
+		//前端页面将自己需要的文件名字拿过来。这个名字直接拼接到文件所在服务器的相对路径。这里为便于测试。我直接把名字写死,以后使用的时候
+
+		//根据实际业务进行修改。
+
+		String fileName = request.getParameter("file");
+
+		System.out.println(fileName);
+
+		try {
+
+			//mac系统,所以路径是这样子的。win系统就是D盘什么什么的
+
+			String path = "C:/Program Files/Apache Software Foundation/Tomcat 8.5/webapps/test"+File.separator+fileName;
+
+			//这里是下载以后的文件叫做什么名字。我这里是以时间来定义名字的。
+
+			downCfg(fileName, request, response);
+
+			OutputStream out;
+
+			FileInputStream inputStream = new FileInputStream(path);
+
+			out = response.getOutputStream();
+
+			byte[] buffer = new byte[1024];
+
+			int len;
+
+			while ((len = inputStream.read(buffer)) != -1) {
+
+				out.write(buffer, 0, len);
+
+			}
+
+			inputStream.close();
+
+			out.close();
+
+			out.flush();
+
+		} catch (IOException e) {
+
+			e.printStackTrace();
+
+		}
+
+	}
+
+
+	private void downCfg(String fileName, HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
+
+		// 判断浏览器,进行不同的加密,这样下载的时候保存的文件名就不会乱码
+
+		String userAgent = request.getHeader("User-Agent");
+
+		// 针对IE或者以IE为内核的浏览器:
+
+		if (userAgent.contains("MSIE") || userAgent.contains("Trident")) {
+
+			fileName = URLEncoder.encode(fileName, "UTF-8");
+
+		} else {
+
+			// 非IE浏览器的处理:
+
+			fileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
+
+		}
+
+		response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));
+
+		response.setContentType("application/octet-stream;charset=utf-8");
+
+		response.setCharacterEncoding("UTF-8");
+
+	}
+
+
+	//断点续传 文件下载
+	public boolean download(HttpServletRequest request, HttpServletResponse response, String fileName) {
+		//文件目录
+		Calendar calendar = Calendar.getInstance();
+		File serverDir = new File(System.getProperty("user.dir") + File.separator
+			+ "uploads" + File.separator
+			+ calendar.get(Calendar.YEAR) + File.separator
+			+ (calendar.get(Calendar.MONTH) + 1));
+		File file = new File(serverDir + File.separator + fileName);
+
+		//下载开始位置
+		long startByte = 0;
+		//下载结束位置
+		long endByte = file.length() - 1;
+
+		//获取下载范围
+		String range = request.getHeader("range");
+		if (range != null && range.contains("bytes=") && range.contains("-")) {
+			range = range.substring(range.lastIndexOf("=") + 1).trim();
+			String rangeArray[] = range.split("-");
+			if (rangeArray.length == 1) {
+				//Example: bytes=1024-
+				if (range.endsWith("-")) {
+					startByte = Long.parseLong(rangeArray[0]);
+				} else { //Example: bytes=-1024
+					endByte = Long.parseLong(rangeArray[0]);
+				}
+			}
+			//Example: bytes=2048-4096
+			else if (rangeArray.length == 2) {
+				startByte = Long.parseLong(rangeArray[0]);
+				endByte = Long.parseLong(rangeArray[1]);
+			}
+		}
+
+		long contentLength = endByte - startByte + 1;
+		String contentType = request.getServletContext().getMimeType(fileName);
+
+		//HTTP 响应头设置
+		//断点续传,HTTP 状态码必须为 206,否则不设置,如果非断点续传设置 206 状态码,则浏览器无法下载
+		if (range != null) {
+			response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
+		}
+		response.setContentType(contentType);
+		response.setHeader("Content-Type", contentType);
+		response.setHeader("Content-Length", String.valueOf(contentLength));
+		response.setHeader("Accept-Ranges", "bytes");
+		//Content-Range: 下载开始位置-下载结束位置/文件大小
+		response.setHeader("Content-Range", "bytes " + startByte + "-" + endByte + "/" + file.length());
+		//Content-disposition: inline; filename=xxx.xxx 表示浏览器内嵌显示该文件
+		//Content-disposition: attachment; filename=xxx.xxx 表示浏览器下载该文件
+		response.setHeader("Content-Disposition", "inline; filename=" + fileName);
+
+		//传输文件流
+		BufferedOutputStream outputStream = null;
+		RandomAccessFile randomAccessFile = null;
+		//已传送数据大小
+		long transmittedLength = 0;
+		try {
+			//以只读模式设置文件指针偏移量
+			randomAccessFile = new RandomAccessFile(file, "r");
+			randomAccessFile.seek(startByte);
+
+			outputStream = new BufferedOutputStream(response.getOutputStream());
+			byte[] buff = new byte[4096];
+			int len;
+			while (transmittedLength < contentLength && (len = randomAccessFile.read(buff)) != -1) {
+				outputStream.write(buff, 0, len);
+				transmittedLength += len;
+			}
+
+			outputStream.flush();
+			response.flushBuffer();
+			logger.info("下载完毕: {}-{}: {}", startByte, endByte, transmittedLength);
+			return true;
+
+		} catch (IOException e) {
+			logger.info("下载停止: {}-{}: {}", startByte, endByte, transmittedLength);
+		} finally {
+			try {
+				if (randomAccessFile != null) {
+					randomAccessFile.close();
+				}
+			} catch (IOException e) {
+				e.printStackTrace();
+			}
+		}
+		return false;
+	}
+
+
+}

+ 17 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/CustomizeColumnWidth.java

@@ -0,0 +1,17 @@
+package org.springblade.common.utils;
+
+import com.alibaba.excel.metadata.CellData;
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
+import org.apache.poi.ss.usermodel.Cell;
+
+import java.util.List;
+
+public class CustomizeColumnWidth extends AbstractColumnWidthStyleStrategy {
+	@Override
+	protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<CellData> list, Cell cell, Head head, Integer integer, Boolean aBoolean) {
+		int cellIndex = cell.getColumnIndex();
+		writeSheetHolder.getSheet().setColumnWidth(cellIndex, 23 * 256);
+	}
+}

+ 16 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/DropDownSetField.java

@@ -0,0 +1,16 @@
+package org.springblade.common.utils;
+
+
+import java.lang.annotation.*;
+
+@Documented
+// 作用在字段上
+@Target(ElementType.FIELD)
+// 运行时有效
+@Retention(RetentionPolicy.RUNTIME)
+public @interface DropDownSetField {
+	// 固定下拉内容
+	String[] source() default {};
+	// 动态下拉内容
+	Class[] sourceClass() default {};
+}

+ 8 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/DropDownSetImpl.java

@@ -0,0 +1,8 @@
+package org.springblade.common.utils;
+
+public class DropDownSetImpl implements DropDownSetInterface {
+	@Override
+	public String[] getSource() {
+		return new String[]{"g","kg","t","ml","l","米","千米"};
+	}
+}

+ 5 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/DropDownSetInterface.java

@@ -0,0 +1,5 @@
+package org.springblade.common.utils;
+
+public interface DropDownSetInterface {
+	String[] getSource();
+}

+ 142 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/HttpClient.java

@@ -0,0 +1,142 @@
+package org.springblade.common.utils;
+
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Random;
+
+public class HttpClient {
+
+	public static String doPost(String httpUrl, String token, String param) {
+
+		HttpURLConnection connection = null;
+		InputStream is = null;
+		OutputStream os = null;
+		BufferedReader br = null;
+		String result = null;
+		try {
+			URL url = new URL(httpUrl);
+			// 通过远程url连接对象打开连接
+			connection = (HttpURLConnection) url.openConnection();
+			// 设置连接请求方式
+			connection.setRequestMethod("POST");
+			// 设置连接主机服务器超时时间:15000毫秒
+			connection.setConnectTimeout(15000);
+			// 设置读取主机服务器返回数据超时时间:60000毫秒
+			connection.setReadTimeout(60000);
+
+			// 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
+			connection.setDoOutput(true);
+			// 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无
+			connection.setDoInput(true);
+			// 设置传入参数的格式
+			connection.setRequestProperty("Content-Type", "application/json");
+			// 设置鉴权信息:
+			connection.setRequestProperty("Access-Token", token);
+			// 通过连接对象获取一个输出流
+			os = connection.getOutputStream();
+			// 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的
+			os.write(param.getBytes("UTF-8"));
+			// 通过连接对象获取一个输入流,向远程读取
+			if (connection.getResponseCode() == 200) {
+
+				is = connection.getInputStream();
+				// 对输入流对象进行包装:charset根据工作项目组的要求来设置
+				br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+
+				StringBuffer sbf = new StringBuffer();
+				String temp = null;
+				// 循环遍历一行一行读取数据
+				while ((temp = br.readLine()) != null) {
+					sbf.append(temp);
+					sbf.append("\r\n");
+				}
+				result = sbf.toString();
+			}
+		} catch (MalformedURLException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		} finally {
+			// 关闭资源
+			if (null != br) {
+				try {
+					br.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+			if (null != os) {
+				try {
+					os.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+			if (null != is) {
+				try {
+					is.close();
+				} catch (IOException e) {
+					e.printStackTrace();
+				}
+			}
+			// 断开与远程地址url的连接
+			connection.disconnect();
+		}
+		return result;
+	}
+
+
+	public static String randomCode() {
+		StringBuilder str = new StringBuilder();
+		Random random = new Random();
+		for (int i = 0; i < 6; i++) {
+			str.append(random.nextInt(10));
+		}
+		return str.toString();
+	}
+
+
+	public static String post(String url, String jsonStr) {
+		// 创建httpClient
+		CloseableHttpClient httpClient = HttpClients.createDefault();
+		// 创建post请求方式实例
+		HttpPost httpPost = new HttpPost(url);
+
+		// 设置请求头 发送的是json数据格式
+		httpPost.setHeader("Content-type", "application/json;charset=utf-8");
+		httpPost.setHeader("Connection", "Close");
+
+		// 设置参数---设置消息实体 也就是携带的数据
+		StringEntity entity = new StringEntity(jsonStr, Charset.forName("UTF-8"));
+		// 设置编码格式
+		entity.setContentEncoding("UTF-8");
+		// 发送Json格式的数据请求
+		entity.setContentType("application/json");
+		// 把请求消息实体塞进去
+		httpPost.setEntity(entity);
+
+		// 执行http的post请求
+		CloseableHttpResponse httpResponse;
+		String result = null;
+		try {
+			httpResponse = httpClient.execute(httpPost);
+			result = EntityUtils.toString(httpResponse.getEntity(), "UTF-8");
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+		return result;
+	}
+
+}

+ 48 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/ProductCellWriteHandler.java

@@ -0,0 +1,48 @@
+package org.springblade.common.utils;
+
+import com.alibaba.excel.write.handler.SheetWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import org.apache.poi.ss.usermodel.DataValidation;
+import org.apache.poi.ss.usermodel.DataValidationConstraint;
+import org.apache.poi.ss.usermodel.DataValidationHelper;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.util.CellRangeAddressList;
+
+import java.util.Map;
+
+public class ProductCellWriteHandler implements SheetWriteHandler {
+	private Map<Integer,String[]> map = null;
+
+	public ProductCellWriteHandler(Map<Integer,String[]> map){
+		this.map = map;
+	}
+
+	@Override
+	public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
+	}
+
+	@Override
+	public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
+		// 这里可以对cell进行任何操作
+		Sheet sheet = writeSheetHolder.getSheet();
+		DataValidationHelper helper = sheet.getDataValidationHelper();
+		// k 为存在下拉数据集的单元格下表 v为下拉数据集
+		map.forEach((k, v) -> {
+			// 下拉列表约束数据
+			DataValidationConstraint constraint = helper.createExplicitListConstraint(v);
+			// 设置下拉单元格的首行 末行 首列 末列
+			CellRangeAddressList rangeList = new CellRangeAddressList(1, 65536, k, k);
+			// 设置约束
+			DataValidation validation = helper.createValidation(constraint, rangeList);
+			// 阻止输入非下拉选项的值
+			validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
+			validation.setShowErrorBox(true);
+			validation.setSuppressDropDownArrow(true);
+			validation.createErrorBox("提示","此值与单元格定义格式不一致");
+			// validation.createPromptBox("填写说明:","填写内容只能为下拉数据集中的单位,其他单位将会导致无法入仓");
+			sheet.addValidationData(validation);
+		});
+	}
+
+}

+ 38 - 0
dietary-nutrition-background/blade-common/src/main/java/org/springblade/common/utils/ResoveDropAnnotationUtil.java

@@ -0,0 +1,38 @@
+package org.springblade.common.utils;
+
+import java.util.Arrays;
+import java.util.Optional;
+
+public class ResoveDropAnnotationUtil {
+
+
+	public static String[] resove(DropDownSetField dropDownSetField){
+		if(!Optional.ofNullable(dropDownSetField).isPresent()){
+			return null;
+		}
+
+		// 获取固定下拉信息
+		String[] source = dropDownSetField.source();
+		if(null != source && source.length > 0){
+			return source;
+		}
+
+		// 获取动态的下拉数据
+		Class<? extends DropDownSetInterface>[] classes = dropDownSetField.sourceClass();
+		if(null != classes && classes.length > 0){
+			try {
+				DropDownSetInterface dropDownSetInterface = Arrays.stream(classes).findFirst().get().newInstance();
+				String[] dynamicSource = dropDownSetInterface.getSource();
+				if(null != dynamicSource && dynamicSource.length > 0){
+					return dynamicSource;
+				}
+			} catch (InstantiationException e) {
+				e.printStackTrace();
+			} catch (IllegalAccessException e) {
+				e.printStackTrace();
+			}
+		}
+		return null;
+	}
+
+}

+ 8 - 0
dietary-nutrition-background/blade-common/src/main/resources/banner.txt

@@ -0,0 +1,8 @@
+${AnsiColor.BLUE}                   ______  _             _       ___   ___
+${AnsiColor.BLUE}                   | ___ \| |           | |      \  \ /  /
+${AnsiColor.BLUE}                   | |_/ /| |  __ _   __| |  ___  \  V  /
+${AnsiColor.BLUE}                   | ___ \| | / _` | / _` | / _ \   > <
+${AnsiColor.BLUE}                   | |_/ /| || (_| || (_| ||  __/ /  .  \
+${AnsiColor.BLUE}                   \____/ |_| \__,_| \__,_| \___|/__/ \__\
+
+${AnsiColor.BLUE}:: BladeX ${blade.service.version} :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK}

+ 15 - 0
dietary-nutrition-background/blade-gateway/Dockerfile

@@ -0,0 +1,15 @@
+FROM adoptopenjdk/openjdk8-openj9:alpine-slim
+
+MAINTAINER smallchill@163.com
+
+RUN mkdir -p /blade/gateway
+
+WORKDIR /blade/gateway
+
+EXPOSE 80
+
+ADD ./target/blade-gateway.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 134 - 0
dietary-nutrition-background/blade-gateway/pom.xml

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-gateway</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-launch</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-web</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.springframework.boot</groupId>
+                    <artifactId>spring-boot-starter-undertow</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.springblade</groupId>
+                    <artifactId>blade-core-launch</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-ribbon</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-jwt</artifactId>
+        </dependency>
+        <!--Spring-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-gateway</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-client</artifactId>
+        </dependency>
+        <!--Hystrix-->
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <!-- Nacos -->
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <!--Swagger-->
+        <dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger2</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>io.swagger</groupId>
+                    <artifactId>swagger-models</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>io.swagger</groupId>
+            <artifactId>swagger-models</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.xiaoymin</groupId>
+            <artifactId>knife4j-spring-ui</artifactId>
+        </dependency>
+        <!--<dependency>
+            <groupId>io.springfox</groupId>
+            <artifactId>springfox-swagger-ui</artifactId>
+            <version>${swagger.version}</version>
+        </dependency>-->
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>dockerfile-maven-plugin</artifactId>
+                <configuration>
+                    <username>${docker.username}</username>
+                    <password>${docker.password}</password>
+                    <repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
+                    <tag>${project.version}</tag>
+                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                    <buildArgs>
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                    </buildArgs>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 39 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java

@@ -0,0 +1,39 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.launch.BladeApplication;
+import org.springframework.cloud.client.SpringCloudApplication;
+import org.springframework.cloud.netflix.hystrix.EnableHystrix;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * 项目启动
+ *
+ * @author Chill
+ */
+@EnableHystrix
+@EnableScheduling
+@SpringCloudApplication
+public class GateWayApplication {
+
+	public static void main(String[] args) {
+		BladeApplication.run(AppConstant.APPLICATION_GATEWAY_NAME, GateWayApplication.class, args);
+	}
+
+}

+ 86 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java

@@ -0,0 +1,86 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.config;
+
+
+import org.springblade.gateway.handler.ErrorExceptionHandler;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.ServerProperties;
+import org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.reactive.error.ErrorAttributes;
+import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.codec.ServerCodecConfigurer;
+import org.springframework.web.reactive.result.view.ViewResolver;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 异常处理配置类
+ *
+ * @author Chill
+ */
+@Configuration
+@AutoConfigureBefore(ErrorWebFluxAutoConfiguration.class)
+@EnableConfigurationProperties({ServerProperties.class, ResourceProperties.class})
+public class ErrorHandlerConfiguration {
+
+	private final ServerProperties serverProperties;
+
+	private final ApplicationContext applicationContext;
+
+	private final ResourceProperties resourceProperties;
+
+	private final List<ViewResolver> viewResolvers;
+
+	private final ServerCodecConfigurer serverCodecConfigurer;
+
+	public ErrorHandlerConfiguration(ServerProperties serverProperties,
+									 ResourceProperties resourceProperties,
+									 ObjectProvider<List<ViewResolver>> viewResolversProvider,
+									 ServerCodecConfigurer serverCodecConfigurer,
+									 ApplicationContext applicationContext) {
+		this.serverProperties = serverProperties;
+		this.applicationContext = applicationContext;
+		this.resourceProperties = resourceProperties;
+		this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
+		this.serverCodecConfigurer = serverCodecConfigurer;
+	}
+
+	@Bean
+	@Order(Ordered.HIGHEST_PRECEDENCE)
+	public ErrorWebExceptionHandler errorWebExceptionHandler(ErrorAttributes errorAttributes) {
+		ErrorExceptionHandler exceptionHandler = new ErrorExceptionHandler(
+			errorAttributes,
+			this.resourceProperties,
+			this.serverProperties.getError(),
+			this.applicationContext);
+		exceptionHandler.setViewResolvers(this.viewResolvers);
+		exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
+		exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
+		return exceptionHandler;
+	}
+
+}

+ 103 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java

@@ -0,0 +1,103 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.config;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.gateway.handler.SwaggerResourceHandler;
+import org.springblade.gateway.handler.SwaggerSecurityHandler;
+import org.springblade.gateway.handler.SwaggerUiHandler;
+import org.springblade.gateway.props.AuthProperties;
+import org.springblade.gateway.props.RouteProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.web.cors.reactive.CorsUtils;
+import org.springframework.web.reactive.function.server.RequestPredicates;
+import org.springframework.web.reactive.function.server.RouterFunction;
+import org.springframework.web.reactive.function.server.RouterFunctions;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+
+/**
+ * 路由配置信息
+ *
+ * @author Chill
+ */
+@Slf4j
+@Configuration
+@AllArgsConstructor
+@EnableConfigurationProperties({RouteProperties.class, AuthProperties.class})
+public class RouterFunctionConfiguration {
+
+	private final SwaggerResourceHandler swaggerResourceHandler;
+	private final SwaggerSecurityHandler swaggerSecurityHandler;
+	private final SwaggerUiHandler swaggerUiHandler;
+
+	/**
+	 * 这里为支持的请求头,如果有自定义的header字段请自己添加
+	 */
+	private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client";
+	private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD";
+	private static final String ALLOWED_ORIGIN = "*";
+	private static final String ALLOWED_EXPOSE = "*";
+	private static final String MAX_AGE = "18000L";
+
+	/**
+	 * 跨域配置
+	 */
+	@Bean
+	public WebFilter corsFilter() {
+		return (ServerWebExchange ctx, WebFilterChain chain) -> {
+			ServerHttpRequest request = ctx.getRequest();
+			if (CorsUtils.isCorsRequest(request)) {
+				ServerHttpResponse response = ctx.getResponse();
+				HttpHeaders headers = response.getHeaders();
+				headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS);
+				headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS);
+				headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN);
+				headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE);
+				headers.add("Access-Control-Max-Age", MAX_AGE);
+				headers.add("Access-Control-Allow-Credentials", "true");
+				if (request.getMethod() == HttpMethod.OPTIONS) {
+					response.setStatusCode(HttpStatus.OK);
+					return Mono.empty();
+				}
+			}
+			return chain.filter(ctx);
+		};
+	}
+
+	@Bean
+	public RouterFunction routerFunction() {
+		return RouterFunctions.route(RequestPredicates.GET("/swagger-resources")
+			.and(RequestPredicates.accept(MediaType.ALL)), swaggerResourceHandler)
+			.andRoute(RequestPredicates.GET("/swagger-resources/configuration/ui")
+				.and(RequestPredicates.accept(MediaType.ALL)), swaggerUiHandler)
+			.andRoute(RequestPredicates.GET("/swagger-resources/configuration/security")
+				.and(RequestPredicates.accept(MediaType.ALL)), swaggerSecurityHandler);
+	}
+
+}

+ 101 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java

@@ -0,0 +1,101 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.ApplicationEventPublisherAware;
+import org.springframework.stereotype.Service;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+/**
+ * 动态路由业务类
+ *
+ * @author Chill
+ */
+@Service
+public class DynamicRouteService implements ApplicationEventPublisherAware {
+
+	private final RouteDefinitionWriter routeDefinitionWriter;
+
+	private ApplicationEventPublisher publisher;
+
+	public DynamicRouteService(RouteDefinitionWriter routeDefinitionWriter) {
+		this.routeDefinitionWriter = routeDefinitionWriter;
+	}
+
+	@Override
+	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
+		this.publisher = applicationEventPublisher;
+	}
+
+	/**
+	 * 增加路由
+	 */
+	public String save(RouteDefinition definition) {
+		try {
+			routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+			this.publisher.publishEvent(new RefreshRoutesEvent(this));
+			return "save success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "save failure";
+		}
+	}
+
+	/**
+	 * 更新路由
+	 */
+	public String update(RouteDefinition definition) {
+		try {
+			this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
+			this.routeDefinitionWriter.save(Mono.just(definition)).subscribe();
+			this.publisher.publishEvent(new RefreshRoutesEvent(this));
+			return "update success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "update failure";
+		}
+	}
+
+	/**
+	 * 更新路由
+	 */
+	public String updateList(List<RouteDefinition> routeDefinitions) {
+		routeDefinitions.forEach(this::update);
+		return "update done";
+	}
+
+	/**
+	 * 删除路由
+	 */
+	public String delete(String id) {
+		try {
+			this.routeDefinitionWriter.delete(Mono.just(id));
+			return "delete success";
+		} catch (Exception e) {
+			e.printStackTrace();
+			return "delete failure";
+		}
+	}
+
+
+}

+ 94 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java

@@ -0,0 +1,94 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import com.alibaba.cloud.nacos.NacosConfigProperties;
+import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.nacos.api.NacosFactory;
+import com.alibaba.nacos.api.PropertyKeyConst;
+import com.alibaba.nacos.api.config.ConfigService;
+import com.alibaba.nacos.api.config.listener.Listener;
+import com.alibaba.nacos.api.exception.NacosException;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.core.launch.constant.NacosConstant;
+import org.springblade.core.launch.props.BladeProperties;
+import org.springframework.cloud.gateway.route.RouteDefinition;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.Executor;
+
+/**
+ * 动态路由监听器
+ *
+ * @author Chill
+ */
+@Order
+@Slf4j
+@Component
+public class DynamicRouteServiceListener {
+
+	private final DynamicRouteService dynamicRouteService;
+	private final NacosDiscoveryProperties nacosDiscoveryProperties;
+	private final NacosConfigProperties nacosConfigProperties;
+	private final BladeProperties bladeProperties;
+
+	public DynamicRouteServiceListener(DynamicRouteService dynamicRouteService, NacosDiscoveryProperties nacosDiscoveryProperties, NacosConfigProperties nacosConfigProperties, BladeProperties bladeProperties) {
+		this.dynamicRouteService = dynamicRouteService;
+		this.nacosDiscoveryProperties = nacosDiscoveryProperties;
+		this.nacosConfigProperties = nacosConfigProperties;
+		this.bladeProperties = bladeProperties;
+		dynamicRouteServiceListener();
+	}
+
+	/**
+	 * 监听Nacos下发的动态路由配置
+	 */
+	private void dynamicRouteServiceListener() {
+		try {
+			String dataId = NacosConstant.dataId(bladeProperties.getName(), bladeProperties.getEnv(), NacosConstant.NACOS_CONFIG_JSON_FORMAT);
+			String group = nacosConfigProperties.getGroup();
+			Properties properties = new Properties();
+			properties.setProperty(PropertyKeyConst.SERVER_ADDR, nacosDiscoveryProperties.getServerAddr());
+			properties.setProperty(PropertyKeyConst.NAMESPACE, nacosDiscoveryProperties.getNamespace());
+			ConfigService configService = NacosFactory.createConfigService(properties);
+			configService.addListener(dataId, group, new Listener() {
+				@Override
+				public void receiveConfigInfo(String configInfo) {
+					List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
+					dynamicRouteService.updateList(routeDefinitions);
+				}
+
+				@Override
+				public Executor getExecutor() {
+					return null;
+				}
+			});
+			String configInfo = configService.getConfig(dataId, group, 5000);
+			if (configInfo != null) {
+				List<RouteDefinition> routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class);
+				dynamicRouteService.updateList(routeDefinitions);
+			}
+		} catch (NacosException ignored) {
+
+		}
+	}
+
+}

+ 41 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 过滤器定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayFilter {
+
+	/**
+	 * 过滤器对应的Name
+	 */
+	private String name;
+
+	/**
+	 * 对应的路由规则
+	 */
+	private Map<String, String> args = new LinkedHashMap<>();
+}

+ 41 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+import lombok.Data;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 路由断言定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayPredicate {
+
+	/**
+	 * 断言对应的Name
+	 */
+	private String name;
+
+	/**
+	 * 配置的断言规则
+	 */
+	private Map<String, String> args = new LinkedHashMap<>();
+}

+ 57 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java

@@ -0,0 +1,57 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.dynamic;
+
+
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Gateway的路由定义模型
+ *
+ * @author Chill
+ */
+@Data
+public class GatewayRoute {
+
+	/**
+	 * 路由的id
+	 */
+	private String id;
+
+	/**
+	 * 路由断言集合配置
+	 */
+	private List<GatewayPredicate> predicates = new ArrayList<>();
+
+	/**
+	 * 路由过滤器集合配置
+	 */
+	private List<GatewayFilter> filters = new ArrayList<>();
+
+	/**
+	 * 路由规则转发的目标uri
+	 */
+	private String uri;
+
+	/**
+	 * 路由执行的顺序
+	 */
+	private int order = 0;
+}

+ 115 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java

@@ -0,0 +1,115 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.filter;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.Claims;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.core.jwt.props.JwtProperties;
+import org.springblade.core.launch.constant.TokenConstant;
+import org.springblade.gateway.props.AuthProperties;
+import org.springblade.gateway.provider.AuthProvider;
+import org.springblade.gateway.provider.RequestProvider;
+import org.springblade.gateway.provider.ResponseProvider;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
+
+import java.nio.charset.StandardCharsets;
+
+/**
+ * 鉴权认证
+ *
+ * @author Chill
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class AuthFilter implements GlobalFilter, Ordered {
+	private final AuthProperties authProperties;
+	private final ObjectMapper objectMapper;
+	private final JwtProperties jwtProperties;
+
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		//校验 Token 放行
+		String originalRequestUrl = RequestProvider.getOriginalRequestUrl(exchange);
+		String path = exchange.getRequest().getURI().getPath();
+		if (isSkip(path) || isSkip(originalRequestUrl)) {
+			return chain.filter(exchange);
+		}
+		//校验 Token 合法性
+		ServerHttpResponse resp = exchange.getResponse();
+		String headerToken = exchange.getRequest().getHeaders().getFirst(AuthProvider.AUTH_KEY);
+		String paramToken = exchange.getRequest().getQueryParams().getFirst(AuthProvider.AUTH_KEY);
+		if (StringUtils.isAllBlank(headerToken, paramToken)) {
+			return unAuth(resp, "缺失令牌,鉴权失败");
+		}
+		String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken;
+		String token = JwtUtil.getToken(auth);
+		Claims claims = JwtUtil.parseJWT(token);
+		if (token == null || claims == null) {
+			return unAuth(resp, "请求未授权");
+		}
+		//判断 Token 状态
+		if (jwtProperties.getState()) {
+			String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID));
+			String userId = String.valueOf(claims.get(TokenConstant.USER_ID));
+			String accessToken = JwtUtil.getAccessToken(tenantId, userId, token);
+			if (!token.equalsIgnoreCase(accessToken)) {
+				return unAuth(resp, "令牌已失效");
+			}
+		}
+		return chain.filter(exchange);
+	}
+
+	private boolean isSkip(String path) {
+		return AuthProvider.getDefaultSkipUrl().stream().map(url -> url.replace(AuthProvider.TARGET, AuthProvider.REPLACEMENT)).anyMatch(path::startsWith)
+			|| authProperties.getSkipUrl().stream().map(url -> url.replace(AuthProvider.TARGET, AuthProvider.REPLACEMENT)).anyMatch(path::startsWith);
+	}
+
+	private Mono<Void> unAuth(ServerHttpResponse resp, String msg) {
+		resp.setStatusCode(HttpStatus.UNAUTHORIZED);
+		resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
+		String result = "";
+		try {
+			result = objectMapper.writeValueAsString(ResponseProvider.unAuth(msg));
+		} catch (JsonProcessingException e) {
+			log.error(e.getMessage(), e);
+		}
+		DataBuffer buffer = resp.bufferFactory().wrap(result.getBytes(StandardCharsets.UTF_8));
+		return resp.writeWith(Flux.just(buffer));
+	}
+
+
+	@Override
+	public int getOrder() {
+		return -100;
+	}
+
+}

+ 112 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalRequestLogFilter.java

@@ -0,0 +1,112 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.gateway.filter;
+
+import io.jsonwebtoken.Claims;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springblade.core.jwt.JwtUtil;
+import org.springblade.gateway.provider.AuthProvider;
+import org.springblade.gateway.provider.RequestProvider;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * webflux 日志请求记录,方便开发调试。请求日志过滤器排序尽量低。
+ *
+ * <p>
+ * 注意:暂时不支持结构体打印,想实现,请看下面的链接。
+ * https://stackoverflow.com/questions/45240005/how-to-log-request-and-response-bodies-in-spring-webflux
+ * https://github.com/Silvmike/webflux-demo/blob/master/tests/src/test/java/ru/hardcoders/demo/webflux/web_handler/filters/logging
+ * </p>
+ *
+ * @author dream.lu
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true)
+public class GlobalRequestLogFilter implements GlobalFilter, Ordered {
+	private final WebEndpointProperties endpointProperties;
+
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		ServerHttpRequest request = exchange.getRequest();
+		// 打印请求路径
+		String path = request.getPath().pathWithinApplication().value();
+
+		// 忽略 endpoint 请求
+		String endpointBasePath = endpointProperties.getBasePath();
+		if (StringUtils.isNotBlank(endpointBasePath) && path.startsWith(endpointBasePath)) {
+			return chain.filter(exchange);
+		}
+
+		String requestUrl = RequestProvider.getOriginalRequestUrl(exchange);
+
+		// 构建成一条长 日志,避免并发下日志错乱
+		StringBuilder beforeReqLog = new StringBuilder(300);
+		// 日志参数
+		List<Object> beforeReqArgs = new ArrayList<>();
+		beforeReqLog.append("\n\n================ Gateway Request Start  ================\n");
+		// 打印路由
+		beforeReqLog.append("===> {}: {}\n");
+		// 参数
+		String requestMethod = request.getMethodValue();
+		beforeReqArgs.add(requestMethod);
+		beforeReqArgs.add(requestUrl);
+
+		// 打印请求头
+		HttpHeaders headers = request.getHeaders();
+		headers.forEach((headerName, headerValue) -> {
+			beforeReqLog.append("===Headers===  {}: {}\n");
+			beforeReqArgs.add(headerName);
+			if (AuthProvider.AUTH_KEY.toLowerCase().equals(headerName)) {
+				String value = headerValue.get(0);
+				String token = JwtUtil.getToken(value);
+				Claims claims = JwtUtil.parseJWT(token);
+				beforeReqArgs.add((claims == null) ? "" : claims.toString());
+				beforeReqLog.append("===Headers===  {}: {}\n");
+				beforeReqArgs.add(headerName.concat("-original"));
+				beforeReqArgs.add(StringUtils.join(headerValue));
+			} else {
+				beforeReqArgs.add(StringUtils.join(headerValue));
+			}
+		});
+
+		beforeReqLog.append("================  Gateway Request End  =================\n");
+		// 打印执行时间
+		log.info(beforeReqLog.toString(), beforeReqArgs.toArray());
+		return chain.filter(exchange);
+	}
+
+	@Override
+	public int getOrder() {
+		return Ordered.LOWEST_PRECEDENCE;
+	}
+}

+ 99 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/GlobalResponseLogFilter.java

@@ -0,0 +1,99 @@
+/*
+ *      Copyright (c) 2018-2028, DreamLu All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: DreamLu 卢春梦 (596392912@qq.com)
+ */
+package org.springblade.gateway.filter;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * webflux 相应日志,方便开发调试,注意排序要优先。
+ *
+ * @author dream.lu
+ */
+@Slf4j
+@Configuration
+@RequiredArgsConstructor
+@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true)
+public class GlobalResponseLogFilter implements GlobalFilter, Ordered {
+	private final WebEndpointProperties endpointProperties;
+
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		ServerHttpRequest request = exchange.getRequest();
+		// 打印请求路径
+		String path = request.getPath().pathWithinApplication().value();
+		// 忽略 endpoint 请求
+		String endpointBasePath = endpointProperties.getBasePath();
+		if (StringUtils.isNotBlank(endpointBasePath) && path.startsWith(endpointBasePath)) {
+			return chain.filter(exchange);
+		}
+		return chain.filter(exchange).then(
+			Mono.fromRunnable(() -> {
+				MultiValueMap<String, String> queryParams = request.getQueryParams();
+				String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString();
+
+				// 构建成一条长 日志,避免并发下日志错乱
+				StringBuilder responseLog = new StringBuilder(300);
+				// 日志参数
+				List<Object> responseArgs = new ArrayList<>();
+				responseLog.append("\n\n================ Gateway Response Start  ================\n");
+				ServerHttpResponse response = exchange.getResponse();
+				// 打印路由 200 get: /api/xxx/xxx
+				responseLog.append("<=== {} {}: {}\n");
+				// 参数
+				String requestMethod = request.getMethodValue();
+				responseArgs.add(response.getStatusCode().value());
+				responseArgs.add(requestMethod);
+				responseArgs.add(requestUrl);
+
+				// 打印请求头
+				HttpHeaders headers = response.getHeaders();
+				headers.forEach((headerName, headerValue) -> {
+					responseLog.append("===Headers===  {}: {}\n");
+					responseArgs.add(headerName);
+					responseArgs.add(StringUtils.join(headerValue));
+				});
+
+				responseLog.append("================  Gateway Response End  =================\n");
+				// 打印执行时间
+				log.info(responseLog.toString(), responseArgs.toArray());
+			})
+		);
+	}
+
+	@Override
+	public int getOrder() {
+		return Ordered.HIGHEST_PRECEDENCE;
+	}
+}

+ 63 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java

@@ -0,0 +1,63 @@
+package org.springblade.gateway.filter;
+
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR;
+import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.addOriginalRequestUrl;
+
+/**
+ * <p>
+ * 全局拦截器,作用所有的微服务
+ * <p>
+ * 1. 对请求头中参数进行处理 from 参数进行清洗
+ * 2. 重写StripPrefix = 1,支持全局
+ *
+ * @author lengleng
+ */
+@Component
+public class RequestFilter implements GlobalFilter, Ordered {
+
+	/**
+	 * Process the Web request and (optionally) delegate to the next
+	 * {@code WebFilter} through the given {@link GatewayFilterChain}.
+	 *
+	 * @param exchange the current server exchange
+	 * @param chain    provides a way to delegate to the next filter
+	 * @return {@code Mono<Void>} to indicate when request processing is complete
+	 */
+	@Override
+	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+		// 1. 清洗请求头中from 参数
+		ServerHttpRequest request = exchange.getRequest().mutate()
+			.headers(httpHeaders -> httpHeaders.remove("X"))
+			.build();
+
+		// 2. 重写StripPrefix
+		addOriginalRequestUrl(exchange, request.getURI());
+		String rawPath = request.getURI().getRawPath();
+		String newPath = "/" + Arrays.stream(StringUtils.tokenizeToStringArray(rawPath, "/"))
+			.skip(1L).collect(Collectors.joining("/"));
+		ServerHttpRequest newRequest = request.mutate()
+			.path(newPath)
+			.build();
+		exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
+
+		return chain.filter(exchange.mutate().request(newRequest.mutate().build()).build());
+	}
+
+	@Override
+	public int getOrder() {
+		return -1000;
+	}
+
+}

+ 101 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java

@@ -0,0 +1,101 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.handler;
+
+import org.springblade.gateway.provider.ResponseProvider;
+import org.springframework.boot.autoconfigure.web.ErrorProperties;
+import org.springframework.boot.autoconfigure.web.ResourceProperties;
+import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
+import org.springframework.boot.web.reactive.error.ErrorAttributes;
+import org.springframework.cloud.gateway.support.NotFoundException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.reactive.function.server.*;
+import org.springframework.web.server.ResponseStatusException;
+
+import java.util.Map;
+
+/**
+ * 异常处理
+ *
+ * @author Chill
+ */
+public class ErrorExceptionHandler extends DefaultErrorWebExceptionHandler {
+
+	public ErrorExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
+								 ErrorProperties errorProperties, ApplicationContext applicationContext) {
+		super(errorAttributes, resourceProperties, errorProperties, applicationContext);
+	}
+
+	/**
+	 * 获取异常属性
+	 */
+	@Override
+	protected Map<String, Object> getErrorAttributes(ServerRequest request, boolean includeStackTrace) {
+		int code = 500;
+		Throwable error = super.getError(request);
+		if (error instanceof NotFoundException) {
+			code = 404;
+		}
+		if (error instanceof ResponseStatusException) {
+			code = ((ResponseStatusException) error).getStatus().value();
+		}
+		return ResponseProvider.response(code, this.buildMessage(request, error));
+	}
+
+	/**
+	 * 指定响应处理方法为JSON处理的方法
+	 *
+	 * @param errorAttributes
+	 */
+	@Override
+	protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
+		return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
+	}
+
+	/**
+	 * 根据code获取对应的HttpStatus
+	 *
+	 * @param errorAttributes
+	 */
+	@Override
+	protected HttpStatus getHttpStatus(Map<String, Object> errorAttributes) {
+		int statusCode = (int) errorAttributes.get("code");
+		return HttpStatus.valueOf(statusCode);
+	}
+
+	/**
+	 * 构建异常信息
+	 *
+	 * @param request
+	 * @param ex
+	 * @return
+	 */
+	private String buildMessage(ServerRequest request, Throwable ex) {
+		StringBuilder message = new StringBuilder("Failed to handle request [");
+		message.append(request.methodName());
+		message.append(" ");
+		message.append(request.uri());
+		message.append("]");
+		if (ex != null) {
+			message.append(": ");
+			message.append(ex.getMessage());
+		}
+		return message.toString();
+	}
+
+}

+ 55 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/SwaggerResourceHandler.java

@@ -0,0 +1,55 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+
+package org.springblade.gateway.handler;
+
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import reactor.core.publisher.Mono;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+/**
+ * SwaggerResourceHandler
+ *
+ * @author lengleng
+ */
+@Slf4j
+@Component
+@AllArgsConstructor
+public class SwaggerResourceHandler implements HandlerFunction<ServerResponse> {
+	private final SwaggerResourcesProvider swaggerResources;
+
+	/**
+	 * Handle the given request.
+	 *
+	 * @param request the request to handler
+	 * @return the response
+	 */
+	@Override
+	public Mono<ServerResponse> handle(ServerRequest request) {
+		return ServerResponse.status(HttpStatus.OK)
+			.contentType(MediaType.APPLICATION_JSON_UTF8)
+			.body(BodyInserters.fromObject(swaggerResources.get()));
+	}
+}

+ 52 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/SwaggerSecurityHandler.java

@@ -0,0 +1,52 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+
+package org.springblade.gateway.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import reactor.core.publisher.Mono;
+import springfox.documentation.swagger.web.SecurityConfigurationBuilder;
+
+/**
+ * SwaggerSecurityHandler
+ *
+ * @author lengleng
+ */
+@Slf4j
+@Component
+public class SwaggerSecurityHandler implements HandlerFunction<ServerResponse> {
+
+	/**
+	 * Handle the given request.
+	 *
+	 * @param request the request to handler
+	 * @return the response
+	 */
+	@Override
+	public Mono<ServerResponse> handle(ServerRequest request) {
+		return ServerResponse.status(HttpStatus.OK)
+			.contentType(MediaType.APPLICATION_JSON_UTF8)
+			.body(BodyInserters.fromObject(SecurityConfigurationBuilder.builder().build()));
+	}
+}

+ 52 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/handler/SwaggerUiHandler.java

@@ -0,0 +1,52 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+
+package org.springblade.gateway.handler;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Component;
+import org.springframework.web.reactive.function.BodyInserters;
+import org.springframework.web.reactive.function.server.HandlerFunction;
+import org.springframework.web.reactive.function.server.ServerRequest;
+import org.springframework.web.reactive.function.server.ServerResponse;
+import reactor.core.publisher.Mono;
+import springfox.documentation.swagger.web.UiConfigurationBuilder;
+
+/**
+ * SwaggerUiHandler
+ *
+ * @author lengleng
+ */
+@Slf4j
+@Component
+public class SwaggerUiHandler implements HandlerFunction<ServerResponse> {
+
+	/**
+	 * Handle the given request.
+	 *
+	 * @param request the request to handler
+	 * @return the response
+	 */
+	@Override
+	public Mono<ServerResponse> handle(ServerRequest request) {
+		return ServerResponse.status(HttpStatus.OK)
+			.contentType(MediaType.APPLICATION_JSON_UTF8)
+			.body(BodyInserters.fromObject(UiConfigurationBuilder.builder().build()));
+	}
+}

+ 41 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java

@@ -0,0 +1,41 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 权限过滤
+ *
+ * @author Chill
+ */
+@Data
+@RefreshScope
+@ConfigurationProperties("blade.secure")
+public class AuthProperties {
+
+	/**
+	 * 放行API集合
+	 */
+	private final List<String> skipUrl = new ArrayList<>();
+
+}

+ 38 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/props/RouteProperties.java

@@ -0,0 +1,38 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.props;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 路由配置类
+ *
+ * @author Chill
+ */
+@Data
+@RefreshScope
+@ConfigurationProperties("blade.document")
+public class RouteProperties {
+
+	private final List<RouteResource> resources = new ArrayList<>();
+
+}

+ 45 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/props/RouteResource.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.props;
+
+import lombok.Data;
+import org.springblade.core.launch.constant.AppConstant;
+
+/**
+ * Swagger聚合文档属性
+ *
+ * @author Chill
+ */
+@Data
+public class RouteResource {
+
+	/**
+	 * 文档名
+	 */
+	private String name;
+
+	/**
+	 * 文档所在服务地址
+	 */
+	private String location;
+
+	/**
+	 * 文档版本
+	 */
+	private String version = AppConstant.APPLICATION_VERSION;
+
+}

+ 78 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java

@@ -0,0 +1,78 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import org.springblade.core.launch.constant.TokenConstant;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 鉴权配置
+ *
+ * @author Chill
+ */
+public class AuthProvider {
+
+	public static final String TARGET = "/**";
+	public static final String REPLACEMENT = "";
+	public static final String AUTH_KEY = TokenConstant.HEADER;
+	private static final List<String> DEFAULT_SKIP_URL = new ArrayList<>();
+
+	static {
+		DEFAULT_SKIP_URL.add("/example");
+		DEFAULT_SKIP_URL.add("/oauth/token/**");
+		DEFAULT_SKIP_URL.add("/oauth/captcha/**");
+		DEFAULT_SKIP_URL.add("/oauth/clear-cache/**");
+		DEFAULT_SKIP_URL.add("/oauth/user-info");
+		DEFAULT_SKIP_URL.add("/oauth/render");
+		DEFAULT_SKIP_URL.add("/oauth/callback");
+		DEFAULT_SKIP_URL.add("/oauth/revoke");
+		DEFAULT_SKIP_URL.add("/oauth/refresh");
+		DEFAULT_SKIP_URL.add("/token/**");
+		DEFAULT_SKIP_URL.add("/actuator/health/**");
+		DEFAULT_SKIP_URL.add("/v2/api-docs/**");
+		DEFAULT_SKIP_URL.add("/v2/api-docs-ext/**");
+		DEFAULT_SKIP_URL.add("/auth/**");
+		DEFAULT_SKIP_URL.add("/log/**");
+		DEFAULT_SKIP_URL.add("/menu/routes");
+		DEFAULT_SKIP_URL.add("/menu/auth-routes");
+		DEFAULT_SKIP_URL.add("/menu/top-menu");
+		DEFAULT_SKIP_URL.add("/tenant/info");
+		DEFAULT_SKIP_URL.add("/process/resource-view");
+		DEFAULT_SKIP_URL.add("/process/diagram-view");
+		DEFAULT_SKIP_URL.add("/manager/check-upload");
+		DEFAULT_SKIP_URL.add("/error/**");
+		DEFAULT_SKIP_URL.add("/assets/**");
+		DEFAULT_SKIP_URL.add("/vilate-code/**");
+		DEFAULT_SKIP_URL.add("/send-msg/**");
+		DEFAULT_SKIP_URL.add("/vilate-phone/**");
+		DEFAULT_SKIP_URL.add("/getRecipeList/**");
+		DEFAULT_SKIP_URL.add("/recipe/**");
+		DEFAULT_SKIP_URL.add("/parent/**");
+		DEFAULT_SKIP_URL.add("/appUserTenant/**");
+
+	}
+
+	/**
+	 * 默认无需鉴权的API
+	 */
+	public static List<String> getDefaultSkipUrl() {
+		return DEFAULT_SKIP_URL;
+	}
+
+}

+ 49 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java

@@ -0,0 +1,49 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.net.URI;
+import java.util.LinkedHashSet;
+
+/**
+ * RequestProvider
+ *
+ * @author Chill
+ */
+public class RequestProvider {
+
+	/**
+	 * 获取原始url
+	 *
+	 * @param exchange
+	 * @return
+	 */
+	public static String getOriginalRequestUrl(ServerWebExchange exchange) {
+		ServerHttpRequest request = exchange.getRequest();
+		LinkedHashSet<URI> uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
+		URI requestUri = uris.stream().findFirst().orElse(request.getURI());
+		MultiValueMap<String, String> queryParams = request.getQueryParams();
+		return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString();
+	}
+
+}

+ 84 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java

@@ -0,0 +1,84 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 请求响应返回
+ *
+ * @author Chill
+ */
+public class ResponseProvider {
+
+	/**
+	 * 成功
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> success(String message) {
+		return response(200, message);
+	}
+
+	/**
+	 * 失败
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> fail(String message) {
+		return response(400, message);
+	}
+
+	/**
+	 * 未授权
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> unAuth(String message) {
+		return response(401, message);
+	}
+
+	/**
+	 * 服务器异常
+	 *
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> error(String message) {
+		return response(500, message);
+	}
+
+	/**
+	 * 构建返回的JSON数据格式
+	 *
+	 * @param status  状态码
+	 * @param message 信息
+	 * @return
+	 */
+	public static Map<String, Object> response(int status, String message) {
+		Map<String, Object> map = new HashMap<>(16);
+		map.put("code", status);
+		map.put("message", message);
+		map.put("data", null);
+		return map;
+	}
+
+}

+ 59 - 0
dietary-nutrition-background/blade-gateway/src/main/java/org/springblade/gateway/provider/SwaggerProvider.java

@@ -0,0 +1,59 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.gateway.provider;
+
+import lombok.AllArgsConstructor;
+import org.springblade.gateway.props.RouteProperties;
+import org.springblade.gateway.props.RouteResource;
+import org.springframework.context.annotation.Primary;
+import org.springframework.stereotype.Component;
+import springfox.documentation.swagger.web.SwaggerResource;
+import springfox.documentation.swagger.web.SwaggerResourcesProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 聚合接口文档注册
+ *
+ * @author Chill
+ */
+@Primary
+@Component
+@AllArgsConstructor
+public class SwaggerProvider implements SwaggerResourcesProvider {
+	private static final String API_URI = "/v2/api-docs-ext";
+
+	private final RouteProperties routeProperties;
+
+	@Override
+	public List<SwaggerResource> get() {
+		List<SwaggerResource> resources = new ArrayList<>();
+		List<RouteResource> routeResources = routeProperties.getResources();
+		routeResources.forEach(routeResource -> resources.add(swaggerResource(routeResource)));
+		return resources;
+	}
+
+	private SwaggerResource swaggerResource(RouteResource routeResource) {
+		SwaggerResource swaggerResource = new SwaggerResource();
+		swaggerResource.setName(routeResource.getName());
+		swaggerResource.setLocation(routeResource.getLocation().concat(API_URI));
+		swaggerResource.setSwaggerVersion(routeResource.getVersion());
+		return swaggerResource;
+	}
+
+}

+ 10 - 0
dietary-nutrition-background/blade-gateway/src/main/resources/application-dev.yml

@@ -0,0 +1,10 @@
+blade:
+  #多团队协作服务配置
+  ribbon:
+    rule:
+      #开启配置
+      enabled: true
+      #负载均衡优先调用的ip段
+      prior-ip-pattern:
+        - 192.168.0.*
+        - 127.0.0.1

+ 12 - 0
dietary-nutrition-background/blade-gateway/src/main/resources/bootstrap.yml

@@ -0,0 +1,12 @@
+server:
+  port: 81
+
+spring:
+  cloud:
+    gateway:
+      discovery:
+        locator:
+          enabled: true
+    loadbalancer:
+      retry:
+        enabled: true

+ 19 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/pom.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>blade-ops-api</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-flow-api</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+
+
+</project>

+ 61 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/constant/ProcessConstant.java

@@ -0,0 +1,61 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.constant;
+
+/**
+ * 流程常量.
+ *
+ * @author Chill
+ */
+public interface ProcessConstant {
+
+	/**
+	 * 请假流程标识
+	 */
+	String LEAVE_KEY = "Leave";
+
+	/**
+	 * 报销流程标识
+	 */
+	String EXPENSE_KEY = "Expense";
+
+	/**
+	 * 同意标识
+	 */
+	String PASS_KEY = "pass";
+
+	/**
+	 * 同意代号
+	 */
+	String PASS_ALIAS = "ok";
+
+	/**
+	 * 同意默认批复
+	 */
+	String PASS_COMMENT = "同意";
+
+	/**
+	 * 驳回默认批复
+	 */
+	String NOT_PASS_COMMENT = "驳回";
+
+	/**
+	 * 创建人变量名
+	 */
+	String TASK_VARIABLE_CREATE_USER = "createUser";
+
+}

+ 179 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/BladeFlow.java

@@ -0,0 +1,179 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.entity;
+
+import lombok.Data;
+import org.springblade.flow.core.constant.ProcessConstant;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+/**
+ * 工作流通用实体类
+ *
+ * @author Chill
+ */
+@Data
+public class BladeFlow implements Serializable {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 任务编号
+	 */
+	private String taskId;
+	/**
+	 * 任务名称
+	 */
+	private String taskName;
+	/**
+	 * 任务定义Key
+	 */
+	private String taskDefinitionKey;
+	/**
+	 * 任务执行人编号
+	 */
+	private String assignee;
+	/**
+	 * 任务执行人名称
+	 */
+	private String assigneeName;
+	/**
+	 * 流程分类
+	 */
+	private String category;
+	/**
+	 * 流程分类名
+	 */
+	private String categoryName;
+	/**
+	 * 创建时间
+	 */
+	private Date createTime;
+	/**
+	 * 结束时间
+	 */
+	private Date endTime;
+	/**
+	 * 签收时间
+	 */
+	private Date claimTime;
+	/**
+	 * 历史任务结束时间
+	 */
+	private Date historyTaskEndTime;
+	/**
+	 * 执行ID
+	 */
+	private String executionId;
+	/**
+	 * 流程实例ID
+	 */
+	private String processInstanceId;
+	/**
+	 * 流程ID
+	 */
+	private String processDefinitionId;
+	/**
+	 * 流程标识
+	 */
+	private String processDefinitionKey;
+	/**
+	 * 流程名
+	 */
+	private String processDefinitionName;
+	/**
+	 * 流程版本
+	 */
+	private int processDefinitionVersion;
+	/**
+	 * 流程说明
+	 */
+	private String processDefinitionDesc;
+	/**
+	 * 流程简图名
+	 */
+	private String processDefinitionDiagramResName;
+	/**
+	 * 流程重命名
+	 */
+	private String processDefinitionResName;
+	/**
+	 * 历史任务流程实例ID 查看流程图会用到
+	 */
+	private String historyProcessInstanceId;
+	/**
+	 * 流程实例是否结束
+	 */
+	private String processIsFinished;
+	/**
+	 * 历史活动ID
+	 */
+	private String historyActivityId;
+	/**
+	 * 历史活动流程
+	 */
+	private String historyActivityName;
+	/**
+	 * 历史活动耗时
+	 */
+	private String historyActivityDurationTime;
+	/**
+	 * 业务绑定Table
+	 */
+	private String businessTable;
+	/**
+	 * 业务绑定ID
+	 */
+	private String businessId;
+	/**
+	 * 任务状态
+	 */
+	private String status;
+	/**
+	 * 任务意见
+	 */
+	private String comment;
+	/**
+	 * 是否通过
+	 */
+	private boolean isPass;
+	/**
+	 * 是否通过代号
+	 */
+	private String flag;
+	/**
+	 * 开始查询日期
+	 */
+	private Date beginDate;
+	/**
+	 * 结束查询日期
+	 */
+	private Date endDate;
+	/**
+	 * 流程参数
+	 */
+	private Map<String, Object> variables;
+
+	/**
+	 * 获取是否通过
+	 */
+	public boolean isPass() {
+		return ProcessConstant.PASS_ALIAS.equals(flag) || ProcessConstant.PASS_COMMENT.equals(comment);
+	}
+
+}

+ 43 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/entity/FlowEntity.java

@@ -0,0 +1,43 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * FlowEntity
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FlowEntity extends BaseEntity {
+
+	@TableField(exist = false)
+	private BladeFlow flow;
+
+	public BladeFlow getFlow() {
+		if (flow == null) {
+			flow = new BladeFlow();
+		}
+		return flow;
+	}
+
+}

+ 45 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/enums/FlowModeEnum.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 流程类型枚举
+ *
+ * @author Chill
+ */
+@Getter
+@AllArgsConstructor
+public enum FlowModeEnum {
+
+	/**
+	 * 通用流程
+	 */
+	COMMON("common", 1),
+
+	/**
+	 * 定制流程
+	 */
+	CUSTOM("custom", 2),
+	;
+
+	final String name;
+	final int mode;
+
+}

+ 100 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClient.java

@@ -0,0 +1,100 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.feign;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.tool.api.R;
+import org.springblade.flow.core.entity.BladeFlow;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.Map;
+
+/**
+ * 工作流远程调用接口.
+ *
+ * @author Chill
+ */
+@FeignClient(
+	value = AppConstant.APPLICATION_FLOW_NAME,
+	fallback = IFlowClientFallback.class
+)
+public interface IFlowClient {
+
+	String API_PREFIX = "/client";
+	String START_PROCESS_INSTANCE_BY_ID = API_PREFIX + "/start-process-instance-by-id";
+	String START_PROCESS_INSTANCE_BY_KEY = API_PREFIX + "/start-process-instance-by-key";
+	String COMPLETE_TASK = API_PREFIX + "/complete-task";
+	String TASK_VARIABLE = API_PREFIX + "/task-variable";
+	String TASK_VARIABLES = API_PREFIX + "/task-variables";
+
+	/**
+	 * 开启流程
+	 *
+	 * @param processDefinitionId 流程id
+	 * @param businessKey         业务key
+	 * @param variables           参数
+	 * @return BladeFlow
+	 */
+	@PostMapping(START_PROCESS_INSTANCE_BY_ID)
+	R<BladeFlow> startProcessInstanceById(@RequestParam("processDefinitionId") String processDefinitionId, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
+
+	/**
+	 * 开启流程
+	 *
+	 * @param processDefinitionKey 流程标识
+	 * @param businessKey          业务key
+	 * @param variables            参数
+	 * @return BladeFlow
+	 */
+	@PostMapping(START_PROCESS_INSTANCE_BY_KEY)
+	R<BladeFlow> startProcessInstanceByKey(@RequestParam("processDefinitionKey") String processDefinitionKey, @RequestParam("businessKey") String businessKey, @RequestBody Map<String, Object> variables);
+
+	/**
+	 * 完成任务
+	 *
+	 * @param taskId            任务id
+	 * @param processInstanceId 流程实例id
+	 * @param comment           评论
+	 * @param variables         参数
+	 * @return R
+	 */
+	@PostMapping(COMPLETE_TASK)
+	R completeTask(@RequestParam("taskId") String taskId, @RequestParam("processInstanceId") String processInstanceId, @RequestParam("comment") String comment, @RequestBody Map<String, Object> variables);
+
+	/**
+	 * 获取流程变量
+	 *
+	 * @param taskId       任务id
+	 * @param variableName 变量名
+	 * @return R
+	 */
+	@GetMapping(TASK_VARIABLE)
+	R<Object> taskVariable(@RequestParam("taskId") String taskId, @RequestParam("variableName") String variableName);
+
+	/**
+	 * 获取流程变量集合
+	 *
+	 * @param taskId 任务id
+	 * @return R
+	 */
+	@GetMapping(TASK_VARIABLES)
+	R<Map<String, Object>> taskVariables(@RequestParam("taskId") String taskId);
+}

+ 58 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/feign/IFlowClientFallback.java

@@ -0,0 +1,58 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.feign;
+
+import org.springblade.core.tool.api.R;
+import org.springblade.flow.core.entity.BladeFlow;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * 流程远程调用失败处理类
+ *
+ * @author Chill
+ */
+@Component
+public class IFlowClientFallback implements IFlowClient {
+
+	@Override
+	public R<BladeFlow> startProcessInstanceById(String processDefinitionId, String businessKey, Map<String, Object> variables) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R<BladeFlow> startProcessInstanceByKey(String processDefinitionKey, String businessKey, Map<String, Object> variables) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R completeTask(String taskId, String processInstanceId, String comment, Map<String, Object> variables) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R<Object> taskVariable(String taskId, String variableName) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R<Map<String, Object>> taskVariables(String taskId) {
+		return R.fail("远程调用失败");
+	}
+
+}

+ 66 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/FlowUtil.java

@@ -0,0 +1,66 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.utils;
+
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.flow.core.constant.ProcessConstant;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 工作流工具类
+ *
+ * @author Chill
+ */
+public class FlowUtil {
+
+	/**
+	 * 定义流程key对应的表名
+	 */
+	private final static Map<String, String> BUSINESS_TABLE = new HashMap<>();
+
+	static {
+		BUSINESS_TABLE.put(ProcessConstant.LEAVE_KEY, "blade_process_leave");
+	}
+
+	/**
+	 * 通过流程key获取业务表名
+	 *
+	 * @param key 流程key
+	 */
+	public static String getBusinessTable(String key) {
+		String businessTable = BUSINESS_TABLE.get(key);
+		if (Func.isEmpty(businessTable)) {
+			throw new RuntimeException("流程启动失败,未找到相关业务表");
+		}
+		return businessTable;
+	}
+
+	/**
+	 * 获取业务标识
+	 *
+	 * @param businessTable 业务表
+	 * @param businessId    业务表主键
+	 * @return businessKey
+	 */
+	public static String getBusinessKey(String businessTable, String businessId) {
+		return StringUtil.format("{}:{}", businessTable, businessId);
+	}
+
+}

+ 71 - 0
dietary-nutrition-background/blade-ops-api/blade-flow-api/src/main/java/org/springblade/flow/core/utils/TaskUtil.java

@@ -0,0 +1,71 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.flow.core.utils;
+
+import org.springblade.core.secure.utils.AuthUtil;
+import org.springblade.core.tool.utils.Func;
+import org.springblade.core.tool.utils.StringUtil;
+
+import static org.springblade.core.launch.constant.FlowConstant.TASK_USR_PREFIX;
+
+/**
+ * 工作流任务工具类
+ *
+ * @author Chill
+ */
+public class TaskUtil {
+
+	/**
+	 * 获取任务用户格式
+	 *
+	 * @return taskUser
+	 */
+	public static String getTaskUser() {
+		return StringUtil.format("{}{}", TASK_USR_PREFIX, AuthUtil.getUserId());
+	}
+
+	/**
+	 * 获取任务用户格式
+	 *
+	 * @param userId 用户id
+	 * @return taskUser
+	 */
+	public static String getTaskUser(String userId) {
+		return StringUtil.format("{}{}", TASK_USR_PREFIX, userId);
+	}
+
+
+	/**
+	 * 获取用户主键
+	 *
+	 * @param taskUser 任务用户
+	 * @return userId
+	 */
+	public static Long getUserId(String taskUser) {
+		return Func.toLong(StringUtil.removePrefix(taskUser, TASK_USR_PREFIX));
+	}
+
+	/**
+	 * 获取用户组格式
+	 *
+	 * @return candidateGroup
+	 */
+	public static String getCandidateGroup() {
+		return AuthUtil.getUserRole();
+	}
+
+}

+ 28 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/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">
+    <parent>
+        <artifactId>blade-ops-api</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-resource-api</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-sms</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-tenant</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>

+ 71 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Attach.java

@@ -0,0 +1,71 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * 附件表实体类
+ *
+ * @author Chill
+ */
+@Data
+@TableName("blade_attach")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Attach对象", description = "附件表")
+public class Attach extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 附件地址
+	 */
+	@ApiModelProperty(value = "附件地址")
+	private String link;
+	/**
+	 * 附件域名
+	 */
+	@ApiModelProperty(value = "附件域名")
+	private String domain;
+	/**
+	 * 附件名称
+	 */
+	@ApiModelProperty(value = "附件名称")
+	private String name;
+	/**
+	 * 附件原名
+	 */
+	@ApiModelProperty(value = "附件原名")
+	private String originalName;
+	/**
+	 * 附件拓展名
+	 */
+	@ApiModelProperty(value = "附件拓展名")
+	private String extension;
+	/**
+	 * 附件大小
+	 */
+	@ApiModelProperty(value = "附件大小")
+	private Long attachSize;
+
+
+}

+ 87 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Oss.java

@@ -0,0 +1,87 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 实体类
+ *
+ * @author BladeX
+ */
+@Data
+@TableName("blade_oss")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Oss对象", description = "Oss对象")
+public class Oss extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+	/**
+	 * 所属分类
+	 */
+	@ApiModelProperty(value = "所属分类")
+	private Integer category;
+
+	/**
+	 * 资源编号
+	 */
+	@ApiModelProperty(value = "资源编号")
+	private String ossCode;
+
+	/**
+	 * oss地址
+	 */
+	@ApiModelProperty(value = "资源地址")
+	private String endpoint;
+	/**
+	 * accessKey
+	 */
+	@ApiModelProperty(value = "accessKey")
+	private String accessKey;
+	/**
+	 * secretKey
+	 */
+	@ApiModelProperty(value = "secretKey")
+	private String secretKey;
+	/**
+	 * 空间名
+	 */
+	@ApiModelProperty(value = "空间名")
+	private String bucketName;
+	/**
+	 * 应用ID TencentCOS需要
+	 */
+	@ApiModelProperty(value = "应用ID")
+	private String appId;
+	/**
+	 * 地域简称 TencentCOS需要
+	 */
+	@ApiModelProperty(value = "地域简称")
+	private String region;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 82 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/entity/Sms.java

@@ -0,0 +1,82 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.core.tenant.mp.TenantEntity;
+
+/**
+ * 短信配置表实体类
+ *
+ * @author BladeX
+ */
+@Data
+@TableName("blade_sms")
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "Sms对象", description = "短信配置表")
+public class Sms extends TenantEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 资源编号
+	 */
+	@ApiModelProperty(value = "资源编号")
+	private String smsCode;
+
+	/**
+	 * 模板ID
+	 */
+	@ApiModelProperty(value = "模板ID")
+	private String templateId;
+	/**
+	 * 分类
+	 */
+	@ApiModelProperty(value = "分类")
+	private Integer category;
+	/**
+	 * accessKey
+	 */
+	@ApiModelProperty(value = "accessKey")
+	private String accessKey;
+	/**
+	 * secretKey
+	 */
+	@ApiModelProperty(value = "secretKey")
+	private String secretKey;
+	/**
+	 * regionId
+	 */
+	@ApiModelProperty(value = "regionId")
+	private String regionId;
+	/**
+	 * 短信签名
+	 */
+	@ApiModelProperty(value = "短信签名")
+	private String signName;
+	/**
+	 * 备注
+	 */
+	@ApiModelProperty(value = "备注")
+	private String remark;
+
+
+}

+ 62 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/enums/SmsCodeEnum.java

@@ -0,0 +1,62 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.springblade.core.tool.utils.StringPool;
+
+/**
+ * Sms资源编码枚举类
+ *
+ * @author Chill
+ * @apiNote 该枚举类对应短信配置模块的资源编码,可根据业务需求自行拓展
+ */
+@Getter
+@AllArgsConstructor
+public enum SmsCodeEnum {
+
+	/**
+	 * 默认编号
+	 */
+	DEFAULT(StringPool.EMPTY, 1),
+
+	/**
+	 * 验证码编号
+	 */
+	VALIDATE("validate", 2),
+
+	/**
+	 * 通知公告编号
+	 */
+	NOTICE("notice", 3),
+
+	/**
+	 * 下单通知编号
+	 */
+	ORDER("order", 4),
+
+	/**
+	 * 会议通知编号
+	 */
+	MEETING("meeting", 5),
+	;
+
+	final String name;
+	final int category;
+
+}

+ 73 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClient.java

@@ -0,0 +1,73 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.feign;
+
+import org.springblade.core.launch.constant.AppConstant;
+import org.springblade.core.sms.model.SmsResponse;
+import org.springblade.core.tool.api.R;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+/**
+ * ISmsClient
+ *
+ * @author Chill
+ */
+@FeignClient(
+	value = AppConstant.APPLICATION_RESOURCE_NAME,
+	fallback = ISmsClientFallback.class
+)
+public interface ISmsClient {
+	String API_PREFIX = "/client";
+	String SEND_MESSAGE = API_PREFIX + "/send-message";
+	String SEND_VALIDATE = API_PREFIX + "/send-validate";
+	String VALIDATE_MESSAGE = API_PREFIX + "/validate-message";
+
+	/**
+	 * 通用短信发送
+	 *
+	 * @param code   资源编号
+	 * @param params 模板参数
+	 * @param phones 手机号集合
+	 * @return R
+	 */
+	@PostMapping(SEND_MESSAGE)
+	R<SmsResponse> sendMessage(@RequestParam("code") String code, @RequestParam("params") String params, @RequestParam("phones") String phones);
+
+	/**
+	 * 短信验证码发送
+	 *
+	 * @param code  资源编号
+	 * @param phone 手机号
+	 * @return R
+	 */
+	@PostMapping(SEND_VALIDATE)
+	R sendValidate(@RequestParam("code") String code, @RequestParam("phone") String phone);
+
+	/**
+	 * 校验短信
+	 *
+	 * @param code  资源编号
+	 * @param id    校验id
+	 * @param value 校验值
+	 * @return R
+	 */
+	@PostMapping(VALIDATE_MESSAGE)
+	R validateMessage(@RequestParam("code") String code, @RequestParam("id") String id, @RequestParam("value") String value);
+
+}

+ 43 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/feign/ISmsClientFallback.java

@@ -0,0 +1,43 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.feign;
+
+import org.springblade.core.tool.api.R;
+import org.springframework.stereotype.Component;
+
+/**
+ * 流程远程调用失败处理类
+ *
+ * @author Chill
+ */
+@Component
+public class ISmsClientFallback implements ISmsClient {
+	@Override
+	public R sendMessage(String code, String params, String phones) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R sendValidate(String code, String phone) {
+		return R.fail("远程调用失败");
+	}
+
+	@Override
+	public R validateMessage(String code, String id, String value) {
+		return R.fail("远程调用失败");
+	}
+}

+ 113 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/utils/SmsUtil.java

@@ -0,0 +1,113 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.utils;
+
+import org.springblade.core.sms.model.SmsCode;
+import org.springblade.core.sms.model.SmsResponse;
+import org.springblade.core.tool.api.R;
+import org.springblade.core.tool.jackson.JsonUtil;
+import org.springblade.core.tool.utils.RandomType;
+import org.springblade.core.tool.utils.SpringUtil;
+import org.springblade.core.tool.utils.StringUtil;
+import org.springblade.resource.feign.ISmsClient;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 短信服务工具类
+ *
+ * @author Chill
+ */
+public class SmsUtil {
+
+	public static final String PARAM_KEY = "code";
+	public static final String SEND_SUCCESS = "短信发送成功";
+	public static final String SEND_FAIL = "短信发送失败";
+	public static final String VALIDATE_SUCCESS = "短信校验成功";
+	public static final String VALIDATE_FAIL = "短信校验失败";
+
+	private static ISmsClient smsClient;
+
+	/**
+	 * 获取短信服务构建类
+	 *
+	 * @return SmsBuilder
+	 */
+	public static ISmsClient getSmsClient() {
+		if (smsClient == null) {
+			smsClient = SpringUtil.getBean(ISmsClient.class);
+		}
+		return smsClient;
+	}
+
+	/**
+	 * 获取短信验证码参数
+	 *
+	 * @return 验证码参数
+	 */
+	public static Map<String, String> getValidateParams() {
+		Map<String, String> params = new HashMap<>(1);
+		params.put(PARAM_KEY, StringUtil.random(6, RandomType.INT));
+		return params;
+	}
+
+	/**
+	 * 发送短信
+	 *
+	 * @param code   资源编号
+	 * @param params 模板参数
+	 * @param phones 手机号集合
+	 * @return 发送结果
+	 */
+	public static SmsResponse sendMessage(String code, Map<String, String> params, String phones) {
+		R<SmsResponse> result = getSmsClient().sendMessage(code, JsonUtil.toJson(params), phones);
+		return result.getData();
+	}
+
+	/**
+	 * 发送验证码
+	 *
+	 * @param code  资源编号
+	 * @param phone 手机号
+	 * @return 发送结果
+	 */
+	public static SmsCode sendValidate(String code, String phone) {
+		SmsCode smsCode = new SmsCode();
+		R result = getSmsClient().sendValidate(code, phone);
+		if (result.isSuccess()) {
+			smsCode = JsonUtil.parse(JsonUtil.toJson(result.getData()), SmsCode.class);
+		} else {
+			smsCode.setSuccess(Boolean.FALSE);
+		}
+		return smsCode;
+	}
+
+	/**
+	 * 校验短信
+	 *
+	 * @param code  资源编号
+	 * @param id    校验id
+	 * @param value 校验值
+	 * @return 发送结果
+	 */
+	public static boolean validateMessage(String code, String id, String value) {
+		R result = getSmsClient().validateMessage(code, id, value);
+		return result.isSuccess();
+	}
+
+}

+ 35 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/AttachVO.java

@@ -0,0 +1,35 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.resource.entity.Attach;
+
+/**
+ * 附件表视图实体类
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "AttachVO对象", description = "附件表")
+public class AttachVO extends Attach {
+	private static final long serialVersionUID = 1L;
+
+}

+ 29 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/OssVO.java

@@ -0,0 +1,29 @@
+package org.springblade.resource.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.resource.entity.Oss;
+
+/**
+ * OssVO
+ *
+ * @author Chill
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "OssVO对象", description = "对象存储表")
+public class OssVO extends Oss {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 分类名
+	 */
+	private String categoryName;
+
+	/**
+	 * 是否启用
+	 */
+	private String statusName;
+
+}

+ 45 - 0
dietary-nutrition-background/blade-ops-api/blade-resource-api/src/main/java/org/springblade/resource/vo/SmsVO.java

@@ -0,0 +1,45 @@
+/*
+ *      Copyright (c) 2018-2028, Chill Zhuang All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions are met:
+ *
+ *  Redistributions of source code must retain the above copyright notice,
+ *  this list of conditions and the following disclaimer.
+ *  Redistributions in binary form must reproduce the above copyright
+ *  notice, this list of conditions and the following disclaimer in the
+ *  documentation and/or other materials provided with the distribution.
+ *  Neither the name of the dreamlu.net developer nor the names of its
+ *  contributors may be used to endorse or promote products derived from
+ *  this software without specific prior written permission.
+ *  Author: Chill 庄骞 (smallchill@163.com)
+ */
+package org.springblade.resource.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.resource.entity.Sms;
+
+/**
+ * 短信配置表视图实体类
+ *
+ * @author BladeX
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "SmsVO对象", description = "短信配置表")
+public class SmsVO extends Sms {
+	private static final long serialVersionUID = 1L;
+
+	/**
+	 * 分类名
+	 */
+	private String categoryName;
+
+	/**
+	 * 是否启用
+	 */
+	private String statusName;
+
+}

+ 52 - 0
dietary-nutrition-background/blade-ops-api/pom.xml

@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>BladeX</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-ops-api</artifactId>
+    <name>${project.artifactId}</name>
+    <version>2.6.0.RELEASE</version>
+    <packaging>pom</packaging>
+    <description>BladeX 微服务API集合</description>
+
+    <modules>
+        <module>blade-flow-api</module>
+        <module>blade-resource-api</module>
+    </modules>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-starter-mybatis</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-openfeign</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-auto</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                    <finalName>${project.name}</finalName>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 17 - 0
dietary-nutrition-background/blade-ops/blade-admin/DINGDING_README.md

@@ -0,0 +1,17 @@
+## SDK下载
+
+#### Java SDK 下载
+    下载SDK: https://open-doc.dingtalk.com/microapp/faquestions/vzbp02
+    
+## 配置项
+
+#### bootstrap.yml
+
+```
+    spring:
+      boot:
+        admin:
+          notify:
+            dingtalk:
+              webhook-token: ${Your DingDing Robot Token}
+```

+ 15 - 0
dietary-nutrition-background/blade-ops/blade-admin/Dockerfile

@@ -0,0 +1,15 @@
+FROM adoptopenjdk/openjdk8-openj9:alpine-slim
+
+MAINTAINER smallchill@163.com
+
+RUN mkdir -p /blade/admin
+
+WORKDIR /blade/admin
+
+EXPOSE 7002
+
+ADD ./target/blade-admin.jar ./app.jar
+
+ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
+
+CMD ["--spring.profiles.active=test"]

+ 72 - 0
dietary-nutrition-background/blade-ops/blade-admin/pom.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>blade-ops</artifactId>
+        <groupId>org.springblade</groupId>
+        <version>2.6.0.RELEASE</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>blade-admin</artifactId>
+    <name>${project.artifactId}</name>
+    <version>${bladex.project.version}</version>
+    <packaging>jar</packaging>
+
+    <dependencies>
+        <!--Blade-->
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-common</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-launch</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-tool</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springblade</groupId>
+            <artifactId>blade-core-cloud</artifactId>
+        </dependency>
+        <!--Admin-Server-->
+        <dependency>
+            <groupId>de.codecentric</groupId>
+            <artifactId>spring-boot-admin-starter-server</artifactId>
+        </dependency>
+        <!--Taobao-Sdk-->
+        <dependency>
+            <groupId>com.taobao</groupId>
+            <artifactId>taobao-sdk</artifactId>
+            <version>20200415</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>dockerfile-maven-plugin</artifactId>
+                <configuration>
+                    <username>${docker.username}</username>
+                    <password>${docker.password}</password>
+                    <repository>${docker.registry.url}/${docker.namespace}/${project.artifactId}</repository>
+                    <tag>${project.version}</tag>
+                    <useMavenSettingsForAuth>true</useMavenSettingsForAuth>
+                    <buildArgs>
+                        <JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
+                    </buildArgs>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 0 - 0
dietary-nutrition-background/blade-ops/blade-admin/src/main/java/org/springblade/admin/AdminApplication.java


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません