From a7ba0f21e5dc2018f35e9913b33fa5f43fa8be79 Mon Sep 17 00:00:00 2001 From: zhaohu Date: Fri, 12 Jun 2026 16:14:58 +0800 Subject: [PATCH] init --- .editorconfig | 21 + .gitignore | 34 + CLAUDE.md | 324 ++ LICENSE | 35 + blade-biz-common/pom.xml | 45 + .../springblade/common/cache/CacheNames.java | 37 + .../config/BladeCommonConfiguration.java | 41 + .../common/constant/CommonConstant.java | 61 + .../common/constant/LauncherConstant.java | 216 + .../common/launch/LauncherServiceImpl.java | 75 + .../springblade/common/utils/CommonUtil.java | 35 + .../src/main/resources/banner.txt | 8 + .../doc/apollo/blade-apollo-dev.yaml | 3 + blade-example/blade-apollo/pom.xml | 51 + .../org/springblade/ApolloApplication.java | 46 + .../springblade/listener/ApolloListener.java | 52 + .../src/main/resources/application.yml | 13 + blade-example/blade-mqtt-broker/pom.xml | 24 + .../mqtt/broker/MqttBrokerApplication.java | 47 + .../listener/MqttConnectStatusListener.java | 107 + .../listener/MqttServerMessageListener.java | 91 + .../mqtt/broker/service/IMqttAuthService.java | 49 + .../service/impl/MqttAuthServiceImpl.java | 67 + .../src/main/resources/application.yml | 45 + blade-example/blade-mqtt-client/pom.xml | 24 + .../mqtt/client/MqttClientApplication.java | 47 + .../mqtt/client/listener/DeviceListener.java | 98 + .../client/simulator/DeviceSimulator.java | 282 + .../mqtt/client/support/DataReq.java | 67 + .../mqtt/client/support/NtpReq.java | 45 + .../mqtt/client/support/SysBean.java | 62 + .../src/main/resources/application.yml | 29 + blade-example/blade-rabbit-listener/pom.xml | 27 + .../listener/RabbitListenerApplication.java | 47 + .../config/RabbitListenerConfiguration.java | 92 + .../listener/listener/MessageConsumer.java | 116 + .../src/main/resources/application.yml | 22 + blade-example/blade-rabbit-publisher/pom.xml | 27 + .../publisher/RabbitPublisherApplication.java | 49 + .../config/RabbitPublisherConfiguration.java | 50 + .../service/RabbitMessageService.java | 90 + .../publisher/simulator/MessageSimulator.java | 192 + .../rabbit/publisher/support/MessageData.java | 69 + .../src/main/resources/application.yml | 15 + blade-example/blade-seata-order/README.md | 30 + blade-example/blade-seata-order/pom.xml | 27 + .../seata/order/SeataOrderApplication.java | 37 + .../order/controller/OrderController.java | 34 + .../springblade/seata/order/entity/Order.java | 33 + .../seata/order/feign/IStorageClient.java | 25 + .../order/feign/StorageClientFallback.java | 18 + .../seata/order/mapper/OrderMapper.java | 12 + .../seata/order/mapper/OrderMapper.xml | 5 + .../seata/order/service/IOrderService.java | 23 + .../order/service/impl/OrderServiceImpl.java | 48 + .../src/main/resources/application-dev.yml | 15 + blade-example/blade-seata-storage/README.md | 33 + blade-example/blade-seata-storage/pom.xml | 28 + .../storage/SeataStorageApplication.java | 37 + .../storage/controller/StorageController.java | 31 + .../seata/storage/entity/Storage.java | 26 + .../seata/storage/mapper/StorageMapper.java | 12 + .../seata/storage/mapper/StorageMapper.xml | 5 + .../storage/service/IStorageService.java | 22 + .../service/impl/StorageServiceImpl.java | 30 + .../src/main/resources/application-dev.yml | 15 + blade-example/blade-sharding-jdbc/pom.xml | 41 + .../sharding/ShardingApplication.java | 46 + .../sharding/controller/NoticeController.java | 150 + .../springblade/sharding/entity/Notice.java | 81 + .../sharding/mapper/NoticeMapper.java | 51 + .../sharding/mapper/NoticeMapper.xml | 50 + .../sharding/service/INoticeService.java | 49 + .../service/impl/NoticeServiceImpl.java | 52 + .../org/springblade/sharding/vo/NoticeVO.java | 23 + .../src/main/resources/application-dev.yml | 61 + .../src/main/resources/application-prod.yml | 71 + .../src/main/resources/application-test.yml | 88 + .../src/main/resources/application.yml | 18 + blade-example/blade-xxl-job/Dockerfile | 13 + blade-example/blade-xxl-job/doc/README.md | 22 + .../doc/sql/xxljob.all.create.sql | 123 + blade-example/blade-xxl-job/pom.xml | 59 + .../springblade/xxljob/XxlJobApplication.java | 44 + .../xxljob/config/XxlJobConfig.java | 77 + .../xxljob/handler/SampleXxlJob.java | 256 + .../src/main/resources/application.yml | 25 + .../src/main/resources/logback.xml | 40 + blade-example/pom.xml | 36 + blade-gateway/Dockerfile | 15 + blade-gateway/pom.xml | 103 + .../gateway/GateWayApplication.java | 48 + .../config/ErrorHandlerConfiguration.java | 53 + .../ReactiveDiscoveryConfiguration.java | 50 + .../config/RouterFunctionConfiguration.java | 57 + .../gateway/dynamic/DynamicRouteService.java | 117 + .../dynamic/DynamicRouteServiceListener.java | 109 + .../gateway/dynamic/GatewayFilter.java | 50 + .../gateway/dynamic/GatewayPredicate.java | 50 + .../gateway/dynamic/GatewayRoute.java | 66 + .../gateway/filter/AuthFilter.java | 156 + .../gateway/filter/GatewayFilter.java | 171 + .../gateway/filter/RequestFilter.java | 63 + .../gateway/filter/RequestLogFilter.java | 121 + .../gateway/filter/ResponseLogFilter.java | 108 + .../handler/ErrorExceptionHandler.java | 101 + .../gateway/props/AuthProperties.java | 68 + .../gateway/props/RequestProperties.java | 68 + .../gateway/provider/AuthProvider.java | 71 + .../gateway/provider/AuthSecure.java | 46 + .../gateway/provider/BasicSecure.java | 46 + .../gateway/provider/KeyProvider.java | 303 + .../gateway/provider/RequestProvider.java | 58 + .../gateway/provider/ResponseProvider.java | 93 + .../gateway/provider/SignSecure.java | 46 + .../src/main/resources/application-dev.yml | 11 + .../src/main/resources/application-prod.yml | 11 + .../src/main/resources/application-test.yml | 11 + .../src/main/resources/application.yml | 16 + .../src/main/resources/bootstrap.yml | 39 + blade-service-api/blade-demo-api/pom.xml | 16 + .../com/example/demo/feign/INoticeClient.java | 59 + .../com/example/demo/pojo/entity/Notice.java | 75 + .../com/example/demo/pojo/vo/NoticeVO.java | 20 + blade-service-api/blade-zhaocai-api/pom.xml | 16 + .../zhaocai/pojo/entity/ProcurementPlan.java | 117 + .../pojo/entity/ProcurementPlanDetail.java | 125 + .../pojo/vo/ProcurementPlanDetailVO.java | 47 + .../zhaocai/pojo/vo/ProcurementPlanVO.java | 54 + blade-service-api/pom.xml | 70 + blade-service/blade-demo/Dockerfile | 13 + .../blade-demo/doc/nacos/blade-demo-dev.yaml | 19 + blade-service/blade-demo/pom.xml | 87 + .../com/example/demo/DemoApplication.java | 45 + .../demo/config/DemoConfiguration.java | 48 + .../demo/controller/DemoController.java | 68 + .../demo/controller/DynamicController.java | 76 + .../demo/controller/LiteFlowController.java | 47 + .../demo/controller/LiteRuleController.java | 156 + .../demo/controller/NoticeController.java | 130 + .../demo/controller/UploadController.java | 57 + .../com/example/demo/feign/NoticeClient.java | 56 + .../launcher/DemoLauncherServiceImpl.java | 69 + .../com/example/demo/mapper/NoticeMapper.java | 56 + .../com/example/demo/mapper/NoticeMapper.xml | 39 + .../example/demo/props/DemoProperties.java | 18 + .../example/demo/rule/liteflow/AbizRule.java | 48 + .../example/demo/rule/liteflow/BbizRule.java | 57 + .../example/demo/rule/liteflow/CbizRule.java | 53 + .../example/demo/rule/liteflow/DbizRule.java | 54 + .../rule/liteflow/context/BizContext.java | 21 + .../demo/rule/literule/AlipayRule.java | 26 + .../demo/rule/literule/BankPayRule.java | 26 + .../demo/rule/literule/OrderAmountRule.java | 53 + .../demo/rule/literule/OrderValidateRule.java | 52 + .../demo/rule/literule/PaymentSwitchRule.java | 45 + .../demo/rule/literule/WechatPayRule.java | 26 + .../literule/builder/OrderRuleBuilder.java | 25 + .../demo/rule/literule/context/Address.java | 22 + .../rule/literule/context/OrderContext.java | 24 + .../example/demo/service/IDynamicService.java | 54 + .../example/demo/service/INoticeService.java | 47 + .../demo/service/impl/DynamicServiceImpl.java | 30 + .../demo/service/impl/NoticeServiceImpl.java | 48 + .../src/main/resources/application-dev.yml | 30 + .../src/main/resources/application-prod.yml | 31 + .../src/main/resources/application-test.yml | 31 + .../src/main/resources/application.yml | 29 + .../src/main/resources/liteflow/demo.el.xml | 9 + .../org/springblade/test/BladeDemoTest.java | 31 + .../org/springblade/test/SensitiveTest.java | 298 + .../launcher/DemoTestLauncherServiceImpl.java | 60 + .../src/test/resources/application-dev.yml | 41 + .../src/test/resources/application-test.yml | 10 + .../src/test/resources/application.yml | 21 + blade-service/blade-zhaocai/pom.xml | 69 + .../zhaocai/ZhaocaiApplication.java | 44 + .../zhaocai/config/ZhaocaiConfiguration.java | 62 + .../controller/ProcurementPlanController.java | 130 + .../mapper/ProcurementPlanDetailMapper.java | 56 + .../mapper/ProcurementPlanDetailMapper.xml | 49 + .../zhaocai/mapper/ProcurementPlanMapper.java | 50 + .../zhaocai/mapper/ProcurementPlanMapper.xml | 52 + .../IProcurementPlanDetailService.java | 56 + .../service/IProcurementPlanService.java | 65 + .../ProcurementPlanDetailServiceImpl.java | 58 + .../impl/ProcurementPlanServiceImpl.java | 102 + .../wrapper/ProcurementPlanWrapper.java | 50 + .../src/main/resources/application-dev.yml | 28 + .../src/main/resources/application-prod.yml | 12 + .../src/main/resources/application-test.yml | 12 + .../src/main/resources/application.yml | 10 + blade-service/pom.xml | 49 + doc/nacos/blade-dev.yaml | 84 + doc/nacos/blade-prod.yaml | 56 + doc/nacos/blade-test.yaml | 54 + doc/nacos/blade.yaml | 249 + doc/nacos/routes/blade-gateway-dev.json | 28 + doc/sql/blade_zhaocai_mysql.sql | 67 + doc/sql/seata/seata.sql | 79 + doc/sql/seata/seata_order.sql | 51 + doc/sql/seata/seata_storage.sql | 57 + pom.xml | 300 + script/docker/app/.env | 2 + script/docker/app/deploy.sh | 166 + script/docker/app/docker-compose.yml | 366 ++ .../app/nacos/conf/application.properties | 55 + script/docker/app/nginx/api/nginx.conf | 77 + script/docker/app/nginx/web/html/index.html | 10 + script/docker/app/nginx/web/nginx.conf | 77 + .../app/prometheus/config/alert_rules.yml | 118 + .../app/prometheus/config/alertmanager.yml | 56 + .../docker/app/prometheus/config/dingtalk.yml | 12 + .../docker/app/prometheus/config/grafana.ini | 849 +++ .../app/prometheus/config/prometheus.yml | 68 + .../docker/app/prometheus/config/wechat.tmpl | 34 + .../prometheus/dashboard/bladex-docker.json | 1705 ++++++ .../app/prometheus/dashboard/bladex-jvm.json | 3825 +++++++++++++ .../prometheus/dashboard/bladex-linux.json | 3841 +++++++++++++ .../prometheus/dashboard/bladex-mysql.json | 5055 +++++++++++++++++ .../prometheus/dashboard/bladex-nacos.json | 4539 +++++++++++++++ script/docker/elk/README.md | 27 + script/docker/elk/deploy.sh | 88 + script/docker/elk/docker-compose.yml | 115 + script/docker/elk/es-master.yml | 28 + script/docker/elk/es-slave1.yml | 28 + script/docker/elk/es-slave2.yml | 28 + script/docker/elk/filebeat.yml | 37 + script/docker/elk/kibana.yml | 8 + script/docker/elk/logstash-filebeat.conf | 23 + script/docker/elk/logstash.yml | 8 + script/docker/elk/undeploy.sh | 16 + script/docker/skywalking/docker-compose.yml | 48 + script/fatjar/service.cmd | 1 + script/fatjar/service.sh | 76 + 235 files changed, 33574 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 LICENSE create mode 100644 blade-biz-common/pom.xml create mode 100644 blade-biz-common/src/main/java/org/springblade/common/cache/CacheNames.java create mode 100644 blade-biz-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java create mode 100644 blade-biz-common/src/main/java/org/springblade/common/constant/CommonConstant.java create mode 100644 blade-biz-common/src/main/java/org/springblade/common/constant/LauncherConstant.java create mode 100644 blade-biz-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java create mode 100644 blade-biz-common/src/main/java/org/springblade/common/utils/CommonUtil.java create mode 100644 blade-biz-common/src/main/resources/banner.txt create mode 100644 blade-example/blade-apollo/doc/apollo/blade-apollo-dev.yaml create mode 100644 blade-example/blade-apollo/pom.xml create mode 100644 blade-example/blade-apollo/src/main/java/org/springblade/ApolloApplication.java create mode 100644 blade-example/blade-apollo/src/main/java/org/springblade/listener/ApolloListener.java create mode 100644 blade-example/blade-apollo/src/main/resources/application.yml create mode 100644 blade-example/blade-mqtt-broker/pom.xml create mode 100644 blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/MqttBrokerApplication.java create mode 100644 blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttConnectStatusListener.java create mode 100644 blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttServerMessageListener.java create mode 100644 blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/IMqttAuthService.java create mode 100644 blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/impl/MqttAuthServiceImpl.java create mode 100644 blade-example/blade-mqtt-broker/src/main/resources/application.yml create mode 100644 blade-example/blade-mqtt-client/pom.xml create mode 100644 blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/MqttClientApplication.java create mode 100644 blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/listener/DeviceListener.java create mode 100644 blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/simulator/DeviceSimulator.java create mode 100644 blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/DataReq.java create mode 100644 blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/NtpReq.java create mode 100644 blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/SysBean.java create mode 100644 blade-example/blade-mqtt-client/src/main/resources/application.yml create mode 100644 blade-example/blade-rabbit-listener/pom.xml create mode 100644 blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/RabbitListenerApplication.java create mode 100644 blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/config/RabbitListenerConfiguration.java create mode 100644 blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/listener/MessageConsumer.java create mode 100644 blade-example/blade-rabbit-listener/src/main/resources/application.yml create mode 100644 blade-example/blade-rabbit-publisher/pom.xml create mode 100644 blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/RabbitPublisherApplication.java create mode 100644 blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/config/RabbitPublisherConfiguration.java create mode 100644 blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/service/RabbitMessageService.java create mode 100644 blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/simulator/MessageSimulator.java create mode 100644 blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/support/MessageData.java create mode 100644 blade-example/blade-rabbit-publisher/src/main/resources/application.yml create mode 100644 blade-example/blade-seata-order/README.md create mode 100644 blade-example/blade-seata-order/pom.xml create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/SeataOrderApplication.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/controller/OrderController.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/entity/Order.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/IStorageClient.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/StorageClientFallback.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.xml create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/IOrderService.java create mode 100644 blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/impl/OrderServiceImpl.java create mode 100644 blade-example/blade-seata-order/src/main/resources/application-dev.yml create mode 100644 blade-example/blade-seata-storage/README.md create mode 100644 blade-example/blade-seata-storage/pom.xml create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/SeataStorageApplication.java create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/controller/StorageController.java create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/entity/Storage.java create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.java create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.xml create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/IStorageService.java create mode 100644 blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/impl/StorageServiceImpl.java create mode 100644 blade-example/blade-seata-storage/src/main/resources/application-dev.yml create mode 100644 blade-example/blade-sharding-jdbc/pom.xml create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/ShardingApplication.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/controller/NoticeController.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/entity/Notice.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.xml create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/INoticeService.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/impl/NoticeServiceImpl.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/vo/NoticeVO.java create mode 100644 blade-example/blade-sharding-jdbc/src/main/resources/application-dev.yml create mode 100644 blade-example/blade-sharding-jdbc/src/main/resources/application-prod.yml create mode 100644 blade-example/blade-sharding-jdbc/src/main/resources/application-test.yml create mode 100644 blade-example/blade-sharding-jdbc/src/main/resources/application.yml create mode 100644 blade-example/blade-xxl-job/Dockerfile create mode 100644 blade-example/blade-xxl-job/doc/README.md create mode 100644 blade-example/blade-xxl-job/doc/sql/xxljob.all.create.sql create mode 100644 blade-example/blade-xxl-job/pom.xml create mode 100644 blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/XxlJobApplication.java create mode 100644 blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/config/XxlJobConfig.java create mode 100644 blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/handler/SampleXxlJob.java create mode 100644 blade-example/blade-xxl-job/src/main/resources/application.yml create mode 100644 blade-example/blade-xxl-job/src/main/resources/logback.xml create mode 100644 blade-example/pom.xml create mode 100644 blade-gateway/Dockerfile create mode 100644 blade-gateway/pom.xml create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/config/ReactiveDiscoveryConfiguration.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/filter/RequestLogFilter.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/filter/ResponseLogFilter.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/AuthSecure.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/BasicSecure.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/KeyProvider.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java create mode 100644 blade-gateway/src/main/java/org/springblade/gateway/provider/SignSecure.java create mode 100644 blade-gateway/src/main/resources/application-dev.yml create mode 100644 blade-gateway/src/main/resources/application-prod.yml create mode 100644 blade-gateway/src/main/resources/application-test.yml create mode 100644 blade-gateway/src/main/resources/application.yml create mode 100644 blade-gateway/src/main/resources/bootstrap.yml create mode 100644 blade-service-api/blade-demo-api/pom.xml create mode 100644 blade-service-api/blade-demo-api/src/main/java/com/example/demo/feign/INoticeClient.java create mode 100644 blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/entity/Notice.java create mode 100644 blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/vo/NoticeVO.java create mode 100644 blade-service-api/blade-zhaocai-api/pom.xml create mode 100644 blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlan.java create mode 100644 blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlanDetail.java create mode 100644 blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanDetailVO.java create mode 100644 blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanVO.java create mode 100644 blade-service-api/pom.xml create mode 100644 blade-service/blade-demo/Dockerfile create mode 100644 blade-service/blade-demo/doc/nacos/blade-demo-dev.yaml create mode 100644 blade-service/blade-demo/pom.xml create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/DemoApplication.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/config/DemoConfiguration.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/controller/DemoController.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/controller/DynamicController.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteFlowController.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteRuleController.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/controller/NoticeController.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/controller/UploadController.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/feign/NoticeClient.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/launcher/DemoLauncherServiceImpl.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.xml create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/props/DemoProperties.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/AbizRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/BbizRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/CbizRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/DbizRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/context/BizContext.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/AlipayRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/BankPayRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderAmountRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderValidateRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/PaymentSwitchRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/WechatPayRule.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/builder/OrderRuleBuilder.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/Address.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/OrderContext.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/service/IDynamicService.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/service/INoticeService.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/service/impl/DynamicServiceImpl.java create mode 100644 blade-service/blade-demo/src/main/java/com/example/demo/service/impl/NoticeServiceImpl.java create mode 100644 blade-service/blade-demo/src/main/resources/application-dev.yml create mode 100644 blade-service/blade-demo/src/main/resources/application-prod.yml create mode 100644 blade-service/blade-demo/src/main/resources/application-test.yml create mode 100644 blade-service/blade-demo/src/main/resources/application.yml create mode 100644 blade-service/blade-demo/src/main/resources/liteflow/demo.el.xml create mode 100644 blade-service/blade-demo/src/test/java/org/springblade/test/BladeDemoTest.java create mode 100644 blade-service/blade-demo/src/test/java/org/springblade/test/SensitiveTest.java create mode 100644 blade-service/blade-demo/src/test/java/org/springblade/test/launcher/DemoTestLauncherServiceImpl.java create mode 100644 blade-service/blade-demo/src/test/resources/application-dev.yml create mode 100644 blade-service/blade-demo/src/test/resources/application-test.yml create mode 100644 blade-service/blade-demo/src/test/resources/application.yml create mode 100644 blade-service/blade-zhaocai/pom.xml create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/ZhaocaiApplication.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/config/ZhaocaiConfiguration.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/controller/ProcurementPlanController.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.xml create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.xml create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanDetailService.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanService.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanDetailServiceImpl.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanServiceImpl.java create mode 100644 blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/wrapper/ProcurementPlanWrapper.java create mode 100644 blade-service/blade-zhaocai/src/main/resources/application-dev.yml create mode 100644 blade-service/blade-zhaocai/src/main/resources/application-prod.yml create mode 100644 blade-service/blade-zhaocai/src/main/resources/application-test.yml create mode 100644 blade-service/blade-zhaocai/src/main/resources/application.yml create mode 100644 blade-service/pom.xml create mode 100644 doc/nacos/blade-dev.yaml create mode 100644 doc/nacos/blade-prod.yaml create mode 100644 doc/nacos/blade-test.yaml create mode 100644 doc/nacos/blade.yaml create mode 100644 doc/nacos/routes/blade-gateway-dev.json create mode 100644 doc/sql/blade_zhaocai_mysql.sql create mode 100644 doc/sql/seata/seata.sql create mode 100644 doc/sql/seata/seata_order.sql create mode 100644 doc/sql/seata/seata_storage.sql create mode 100644 pom.xml create mode 100644 script/docker/app/.env create mode 100644 script/docker/app/deploy.sh create mode 100644 script/docker/app/docker-compose.yml create mode 100644 script/docker/app/nacos/conf/application.properties create mode 100644 script/docker/app/nginx/api/nginx.conf create mode 100644 script/docker/app/nginx/web/html/index.html create mode 100644 script/docker/app/nginx/web/nginx.conf create mode 100644 script/docker/app/prometheus/config/alert_rules.yml create mode 100644 script/docker/app/prometheus/config/alertmanager.yml create mode 100644 script/docker/app/prometheus/config/dingtalk.yml create mode 100644 script/docker/app/prometheus/config/grafana.ini create mode 100644 script/docker/app/prometheus/config/prometheus.yml create mode 100644 script/docker/app/prometheus/config/wechat.tmpl create mode 100644 script/docker/app/prometheus/dashboard/bladex-docker.json create mode 100644 script/docker/app/prometheus/dashboard/bladex-jvm.json create mode 100644 script/docker/app/prometheus/dashboard/bladex-linux.json create mode 100644 script/docker/app/prometheus/dashboard/bladex-mysql.json create mode 100644 script/docker/app/prometheus/dashboard/bladex-nacos.json create mode 100644 script/docker/elk/README.md create mode 100644 script/docker/elk/deploy.sh create mode 100644 script/docker/elk/docker-compose.yml create mode 100644 script/docker/elk/es-master.yml create mode 100644 script/docker/elk/es-slave1.yml create mode 100644 script/docker/elk/es-slave2.yml create mode 100644 script/docker/elk/filebeat.yml create mode 100644 script/docker/elk/kibana.yml create mode 100644 script/docker/elk/logstash-filebeat.conf create mode 100644 script/docker/elk/logstash.yml create mode 100644 script/docker/elk/undeploy.sh create mode 100644 script/docker/skywalking/docker-compose.yml create mode 100644 script/fatjar/service.cmd create mode 100644 script/fatjar/service.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..eae2bb5 --- /dev/null +++ b/.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,yaml}] +indent_size = 2 + +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96a89be --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# maven # +target + +logs + +# windows # +Thumbs.db + +# Mac # +.DS_Store + +# eclipse # +.settings +.project +.classpath +.log +*.class + +# idea # +.idea +*.iml + +# Package Files # +*.jar +*.war +*.ear +/target + +# Flattened pom +.flattened-pom.xml +/**/.flattened-pom.xml + +# ai +.claude diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..7a3378b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,324 @@ +# CLAUDE.md +本文件用于指导 Claude Code (claude.ai/code) 在此代码仓库中工作时的行为规范。 + +> 本规范适用于 BladeX 微服务平台的所有开发任务,为强制性条款。除非用户显式豁免,任何条目都不得忽视或删减。 +> +> 作为 AI 助手参与本项目开发时,你必须: +> - 每次输出前深度理解 BladeX 微服务架构、Spring Cloud 全栈技术特征和多租户业务模型 +> - 当回答依赖外部知识时,先查询 Spring Boot 3.x、Spring Cloud 2025、MyBatis-Plus 等官方文档 +> - 若需求含糊,先复述已知信息并列出关键澄清问题 +> - 面对复杂需求,先拆分为可管理的子任务 +> +> 所有开发内容必须建立在深度思考过的基础之上,禁止机械生成与错误填充。 +> 如果你已了解所有规范,请在用户第一次对话时说明:"我已充分了解 BladeX 微服务平台开发规范。" + +--- + +## 1. 项目概述 + +BladeX 是基于 Spring Cloud 2025 + Spring Boot 3.x 构建的**企业级微服务开发平台**,提供 SaaS 多租户、OAuth2 认证、数据/接口权限、工作流、分布式事务等核心能力。 + +--- + +## 2. 项目架构 + +``` +BladeX/ +├── blade-auth/ # OAuth2 认证授权服务 +├── blade-common/ # 公共模块(常量、工具类、启动配置) +├── blade-gateway/ # Spring Cloud Gateway 网关服务 +├── blade-ops/ # 运维服务模块 +│ ├── blade-admin/ # Spring Boot Admin 监控 +│ ├── blade-develop/ # 代码生成服务 +│ ├── blade-flow/ # Flowable 工作流服务 +│ ├── blade-job/ # 分布式任务调度 +│ ├── blade-log/ # 日志服务 +│ ├── blade-report/ # 报表服务 +│ └── blade-resource/ # 资源管理服务(OSS、SMS) +├── blade-ops-api/ # 运维服务 API 契约 +├── blade-plugin/ / blade-plugin-api/ # 插件扩展模块(预留) +├── blade-service/ # 业务服务模块 +│ ├── blade-desk/ # 工作台服务(通知、流程) +│ └── blade-system/ # 系统管理服务(用户、角色、菜单、租户等) +├── blade-service-api/ # 业务服务 API 契约 +├── doc/ # Nacos 配置 & 多数据库建表脚本 +├── script/ # Docker / FatJar 部署脚本 +└── pom.xml # Maven 父工程配置 +``` + +### 2.1 分层架构 + +每个业务模块遵循标准分层,**API 与 Service 分离**: + +| 层次 | 包路径 | 所在模块 | +| --- | --- | --- | +| Controller | `controller/` | Service 模块 | +| Service / ServiceImpl | `service/` + `service/impl/` | Service 模块 | +| Mapper(接口 + XML 同包) | `mapper/` | Service 模块 | +| Wrapper | `wrapper/` | Service 模块(Entity → VO 转换) | +| Entity / VO / DTO | `pojo/entity/` `pojo/vo/` `pojo/dto/` | **API 模块** | +| Feign Client | `feign/` | **API 模块**(含 Fallback 降级) | +| Cache | `cache/` | **API 模块** | + +### 2.2 API 与 Service 分离原则 + +- **API 模块**(`blade-*-api`):定义 Entity、VO、DTO、Feign 接口、Cache 工具,供其他服务依赖 +- **Service 模块**(`blade-service/*`、`blade-ops/*`):实现 Controller、ServiceImpl、Mapper、Wrapper +- 跨服务调用通过 Feign Client 完成,禁止直接依赖其他 Service 模块 + +> **与 Boot 版的关键区别**:Cloud 采用 API/Service 模块分离,跨服务通过 Feign 调用;Controller 路由无需 `AppConstant` 前缀(网关按服务名路由)。 + +--- + +## 3. 技术栈 + +| 类别 | 技术 | +| --- | --- | +| 基础框架 | Spring Boot 3.2.x / Spring Cloud 2025 / Maven | +| ORM | MyBatis-Plus(禁止 JDBC 直连) | +| 微服务 | Nacos(注册/配置)、Sentinel(熔断)、Seata(分布式事务)、OpenFeign | +| 网关 | Spring Cloud Gateway(WebFlux) | +| 安全 | OAuth2 / JWT(自研 Secure 框架)、数据权限、接口权限 | +| 缓存 | Redis(Protostuff 序列化) | +| 工作流 / 任务调度 | Flowable / PowerJob | +| 数据库连接池 | Druid | +| 文档 | Knife4j(OpenAPI 3.0) | +| 监控 | Spring Boot Admin / Prometheus / ELK / Zipkin | +| 数据库 | MySQL / PostgreSQL / Oracle / SQL Server / 达梦 / 人大金仓 / 崖山 | + +--- + +## 4. 开发规范 + +### 4.1 编写新功能前 + +1. 先阅读目标模块中已有的类,理解其结构、命名和风格 +2. 标准参考模块:`blade-service/blade-system`(Service)、`blade-service-api/blade-system-api`(API) +3. 主动模仿现有代码风格,包括缩进(Java 用 Tab,YAML/JSON 用 2 空格)、注解顺序、Javadoc 格式 +4. 新建类必须包含 BladeX 商业许可证头部和 Javadoc 类注释(`@author Chill`) + +### 4.2 编写完成后 + +1. 使用 `mvn clean compile -DskipTests` 编译验证,编译不通过必须修复 +2. 引入模块依赖前先检查循环依赖,若存在则采用更优方案 +3. 编译通过后将测试交由用户执行,**不得自行执行任何测试** +4. 除非用户明确要求,不应撰写示例或额外文档 + +### 4.3 代码生成 + +当需要生成 CRUD 全套代码(Entity、VO、Service、Controller、Wrapper、Mapper、建表语句等)时,优先使用 **`/blade-design`** skill。该 skill 可根据模块名、实体名和字段列表,自动生成符合 BladeX 框架规范的后端代码和多数据库建表语句,确保生成结果与项目风格完全一致。 + +--- + +## 5. 命名规范 + +### 5.1 包名 + +统一前缀 `org.springblade`,按模块划分:`system`、`desk`、`auth`、`gateway`、`develop`、`flow`、`common` 等 + +### 5.2 类名 + +| 类型 | 规则 | 示例 | +| --- | --- | --- | +| Entity | 直接类名 | `Notice`、`Tenant` | +| VO / DTO | `XxxVO` / `XxxDTO` | `NoticeVO`、`DeptDTO` | +| Service 接口 | `IXxxService` | `INoticeService` | +| Service 实现 | `XxxServiceImpl` | `NoticeServiceImpl` | +| Controller | `XxxController` | `NoticeController` | +| Mapper | `XxxMapper` | `NoticeMapper` | +| Wrapper | `XxxWrapper` | `NoticeWrapper` | +| Feign 接口 / 降级 | `IXxxClient` / `IXxxClientFallback` | `INoticeClient` | +| 表名 | `blade_` + 下划线 | `blade_notice` | + +### 5.3 变量命名 + +- 必须具有明确语义:`Exception exception`(✅)`Exception e`(❌);`List noticeList`(✅)`List list`(❌) +- 冲突时提升语义:`Cache cache; Cache noticeCache`(✅)`Cache cache1; Cache cache2`(❌) + +--- + +## 6. 编码规范 + +### 6.1 注解顺序 + +- **Controller 类**:租户注解(`@TenantDS`/`@NonDS`) → `@RestController` → Lombok(`@AllArgsConstructor`) → 安全注解(`@PreAuth`) → `@RequestMapping` → `@Tag` +- **Controller 方法**:HTTP 方法注解 → `@ApiOperationSupport(order=N)` → `@Operation` +- **Entity 类**:`@Data` → `@EqualsAndHashCode` → `@TableName` → `@Schema` +- **VO 类**:`@Data` → `@EqualsAndHashCode(callSuper = true)` → `@Schema` + +### 6.2 Entity-Service 继承体系(核心) + +Entity 基类的选择**直接决定** Service 和 ServiceImpl 的继承方式,三者必须配套: + +| 场景 | Entity | Service 接口 | ServiceImpl | 示例 | +| --- | --- | --- | --- | --- | +| **多租户业务表** | `extends TenantEntity` | `extends BaseService` | `extends BaseServiceImpl` | Notice, Post, Dept | +| **非租户业务表** | `extends BaseEntity` | `extends BaseService` | `extends BaseServiceImpl` | Tenant | +| **轻量级/关系表** | `implements Serializable` | `extends IService` | `extends ServiceImpl` | RoleMenu, Dict, Role | + +- `TenantEntity` 继承自 `BaseEntity` 并额外包含 `tenantId` +- `BaseEntity` 包含:id, createUser, createDept, createTime, updateUser, updateTime, status, isDeleted +- `BaseService`/`BaseServiceImpl` 是 BladeX 增强版,提供 `deleteLogic()` 等方法,**要求 Entity 继承 BaseEntity 或 TenantEntity** +- `IService`/`ServiceImpl` 是 MyBatis-Plus 原生版,用于 `implements Serializable` 的轻量实体,删除用 `removeByIds()` + +### 6.3 Controller 约定 + +- 继承 `BladeController`,使用 `@AllArgsConstructor` 注入 +- **路由直接使用资源路径**(如 `@RequestMapping("notice")`),网关按服务名路由,无需 `AppConstant` 前缀(Boot 版需要前缀) +- 统一返回 `R` 响应体 +- Entity → VO 转换通过 `XxxWrapper.build().entityVO()` / `.pageVO()` + +### 6.4 Feign Client(Cloud 特有) + +- 定义在 API 模块的 `feign/` 包中 +- `@FeignClient(value = AppConstant.APPLICATION_XXX_NAME)` +- 路径常量定义在接口内:`String API_PREFIX = "/feign/client/xxx"` +- 降级类命名:`IXxxClientFallback` + +### 6.5 依赖注入 + +- Controller 使用 `@AllArgsConstructor` + `private final` 字段 +- Service 需要额外依赖时使用 `@RequiredArgsConstructor` + `private final` 字段 + +### 6.6 Lombok + +禁止手写 getter/setter,统一使用 `@Data`、`@EqualsAndHashCode(callSuper = true)`、`@AllArgsConstructor`、`@RequiredArgsConstructor`、`@Slf4j` 等 + +### 6.7 Java 17 特性 + +- 可使用增强 switch、Text Blocks、Pattern Matching for instanceof、`@Serial` +- **禁止** `var` / `val` 类型推断,所有变量显式声明类型 +- 优先使用 Stream API,Lambda 保持简洁 + +### 6.8 MyBatis-Plus + +- 简单查询使用 `LambdaQueryWrapper`,复杂查询写在 Mapper XML 中 +- Mapper XML 与接口**同包**放置(`src/main/java` 下) +- 分页统一使用 `Condition.getPage(query)` + `Condition.getQueryWrapper()` +- 禁止 JDBC 直连查询 + +### 6.9 日志 + +- 使用 `@Slf4j`,占位符传参(禁止字符串拼接) +- 包含关键业务标识,异常必须携带堆栈,禁止打印敏感信息 + +--- + +## 7. 数据库规范 + +- **表名前缀**:`blade_`(如 `blade_notice`、`blade_user`) +- **业务表必含字段**:`id`(BIGINT)、`tenant_id`(VARCHAR)、`create_user`、`create_dept`、`create_time`、`update_user`、`update_time`、`status`、`is_deleted` +- **主键策略**:雪花算法(`IdType.ASSIGN_ID`),Long 字段用 `@JsonSerialize(using = ToStringSerializer.class)` 防精度丢失 +- **逻辑删除**:`is_deleted` (INT),0 = 未删除,1 = 已删除 +- **索引命名**:唯一索引 `uk_` 前缀,普通索引 `idx_` 前缀 +- **多数据库**:支持 7 种数据库,建表脚本位于 `doc/sql/`,修改表结构时需同步所有脚本 + +--- + +## 8. 多租户与安全 + +- **多租户**:默认字段隔离模式(`tenant_id`),MP 内置方法自动注入;自定义 SQL 需手动 `AuthUtil.getTenantId()` +- **租户注解**:`@TenantDS`(启用数据源切换)/ `@NonDS`(禁用切换) +- **权限注解**:`@PreAuth(menu = "xxx")`(菜单权限)、`@PreAuth(AuthConstant.PERMIT_ALL)`(公开) +- **数据权限**:DataScopeHandler 行级过滤 +- **接口权限**:ApiScopeHandler 端点级控制 + +--- + +## 9. 缓存规范 + +- `CacheUtil` 统一操作,常量在 `CacheConstant` +- 数据变更后清除缓存:`CacheUtil.clear(CACHE_NAME, Boolean.FALSE)` +- 字典翻译:`DictCache.getValue(DictEnum.XXX, value)` + +--- + +## 10. 商业许可头部 + +所有新建的 Java 源文件必须在文件顶部包含以下许可头部: +```java +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +``` + +--- + +## 11. 构建与部署 + +```bash +mvn clean compile -DskipTests # 编译验证 +mvn clean package -DskipTests # 打包 +mvn clean package docker:build -DskipTests # Docker 镜像 +``` + +- Nacos 配置:`doc/nacos/blade-{dev|test|prod}.yaml` +- Docker 部署:`script/docker/app/docker-compose.yml` +- 开发环境默认:Nacos `localhost:8848`、Sentinel `localhost:8858`、Redis `127.0.0.1:6379`、MySQL `localhost:3306/bladex` + +--- + +## 12. Git 提交规范 + +当需要提交代码时,优先使用 **`/blade-commit`** skill,它会自动分析变更内容并生成符合项目规范的 Gitmoji 提交信息。 + +采用 **Gitmoji** 风格,中文描述。格式:`:: 简要描述` + +| Gitmoji | 场景 | 示例 | +| --- | --- |------------------------| +| `:sparkles:` | 新增功能 | `:sparkles: 新增角色授权功能` | +| `:zap:` | 优化重构 | `:zap: 优化字典查询排序逻辑` | +| `:bug:` | 修复缺陷 | `:bug: 修复用户查询未过滤已删除数据` | +| `:tada:` | 版本发布 | `:tada: x.x.x.RELEASE` | +| `:recycle:` | 代码重构 | `:recycle: 重构租户删除逻辑` | + +--- + +## 13. 框架组件速查 + +| 组件 | 用途 | +| --- | --- | +| `R` | 统一 API 响应 | +| `BladeController` | Controller 基类 | +| `TenantEntity` / `BaseEntity` | 实体基类(多租户/非租户) | +| `BaseService` / `BaseServiceImpl` | BladeX 增强 Service(含 deleteLogic) | +| `BaseEntityWrapper` | Entity→VO 转换基类 | +| `BladeUser` | 当前登录用户(含租户信息) | +| `Condition` / `Query` | 查询条件构建 / 分页参数 | +| `CacheUtil` / `DictCache` | 缓存工具 / 字典缓存 | +| `AuthUtil` | 获取当前用户/租户信息 | +| `Func` / `BeanUtil` / `StringUtil` | 通用工具类 | +| `ServiceException` | 业务异常 | +| `@PreAuth` / `@TenantDS` / `@NonDS` | 权限 / 租户数据源切换 | +| `@BladeView` / `@DataRecord` / `@XssIgnore` | 视图控制 / 数据审计 / XSS 跳过 | + +--- + +## 14. 风格一致性 + +1. 风格不确定时,优先查阅现有代码并模仿当前模块的实现方式 +2. 新模块参考 `blade-service/blade-system` 作为标准模板 +3. 现有模块已满足需求时,禁止自写替代实现 +4. 与用户交互全程使用**中文**,代码注释和 Javadoc 亦使用中文 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..aaac4ce --- /dev/null +++ b/LICENSE @@ -0,0 +1,35 @@ +BladeX商业授权许可协议 + +一、 知识产权: +BladeX系列产品知识产权归上海布雷德科技有限公司独立所有 + +二、 许可: +1. 在您完全接受并遵守本协议的基础上,本协议授予您使用BladeX的某些权利和非独占性许可。 +2. 本协议中,将本产品使用用途分为"专业版用途"和"企业版用途"。 +3. "专业版用途"定义:指个人在非团体机构中出于任何合法目的使用本产品(任何目的包括商业目的或非盈利目的)。 +4. "企业版用途"定义:指拥有合法执照的团体机构(例如公司企业、政府、学校、军队、医院、社会团体等各类组织)(不包含集团,若集团使用则需为各个子公司分别购买企业授权)出于任何合法目的使用本产品(任何目的包括商业目的或非盈利目的)。 +5. 若您不能以拥有合法执照的团体机构名义购买企业版,则视为个人名义购买,仅可行使专业版用途。在遵守此协议的前提下,后续有一次机会将企业版授权免费绑定至法人为购买人的新公司,并从专业版用途转为企业版用途。 + +三、 约束和限制: +1. 本产品只能由您为本协议许可的目的而使用,您不得透露给任何第三方; +2. 从本产品取得的任何信息、软件、产品或服务,您不得对其进行修改、改编或基于以上内容创建同种类别的衍生产品并售卖。 +3. 您不得对本产品以及与之关联的商业授权进行发布、出租、销售、分销、抵押、转让、许可或发放子许可证。 +4. 本产品商业授权版可能包含一些独立功能或特性,这些功能只有在您购买商业授权后才可以使用。在未取得商业授权的情况下,您不得使用、尝试使用或复制这些授权版独立功能。 +5. 若您的客户要求以源码方式交付软件,需缴纳企业版授权费用,否则本产品部分不得提供源码。 + +四、 不得用于非法或禁止的用途: +您在使用本产品或服务时,不得将本产品产品或服务用于任何非法用途或本协议条款、条件和声明禁止的用途。 + +五、 免责说明: +1. 本产品按"现状"授予许可,您须自行承担使用本产品的风险。BladeX团队不对此提供任何明示、暗示或任何其它形式的担保和表示。在任何情况下,对于因使用或无法使用本软件而导致的任何损失(包括但不仅限于商业利润损失、业务中断或业务信息丢失),BladeX团队无需向您或任何第三方负责,即使BladeX团队已被告知可能会造成此类损失。在任何情况下, BladeX团队均不就任何直接的、间接的、附带的、后果性的、特别的、惩戒性的和处罚性的损害赔偿承担任何责任,无论该主张是基于保证、合同、侵权(包括疏忽)或是基于其他原因作出。 +2. 本产品可能内置有第三方服务,您应自行评估使用这些第三方服务的风险,由使用此类第三方服务而产生的纠纷,全部责任由您自行承担。 +3. BladeX团队不对使用本产品构建的网站中任何信息内容以及导致的任何版权纠纷、法律争议和后果承担任何责任,全部责任由您自行承担。 +4. BladeX团队可能会经常提供产品更新或升级,但BladeX团队没有为根据本协议许可的产品提供维护或更新的责任。 +5. BladeX团队可能会按照官方制定的答疑规则为您进行答疑,但BladeX团队没有为根据本协议许可的产品提供技术支持的义务或责任。 + +六、 权利和所有权的保留: +BladeX团队保留所有未在本协议中明确授予您的所有权利。BladeX团队保留随时更新本协议的权利,并只需公示于对应产品项目的LICENSE文件,无需征得您的事先同意且无需另行通知,更新后的内容应于公示即时生效。您可以随时访问产品地址并查阅最新版许可条款,在更新生效后您继续使用本产品则被视作您已接受了新的条款。 + +七、 协议终止 +1. 您一旦开始复制、下载、安装或者使用本产品,即被视为完全理解并接受本协议的各项条款,在享有上述条款授予的许可权力同时,也受到相关的约束和限制,本协议许可范围以外的行为,将直接违反本协议并构成侵权。 +2. 一旦您违反本协议的条款,BladeX团队随时可能终止本协议、收回许可和授权,并要求您承担相应法律和经济责任。 diff --git a/blade-biz-common/pom.xml b/blade-biz-common/pom.xml new file mode 100644 index 0000000..4e22fcc --- /dev/null +++ b/blade-biz-common/pom.xml @@ -0,0 +1,45 @@ + + + + BladeX-Biz + org.springblade + ${revision} + + 4.0.0 + + blade-biz-common + ${project.artifactId} + jar + + + + org.springblade + blade-core-launch + + + org.springblade + blade-starter-loadbalancer + + + org.springblade + blade-core-auto + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + ${project.name} + + + + + + diff --git a/blade-biz-common/src/main/java/org/springblade/common/cache/CacheNames.java b/blade-biz-common/src/main/java/org/springblade/common/cache/CacheNames.java new file mode 100644 index 0000000..ed3a1e0 --- /dev/null +++ b/blade-biz-common/src/main/java/org/springblade/common/cache/CacheNames.java @@ -0,0 +1,37 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.common.cache; + +/** + * 缓存名 + * + * @author Chill + */ +public interface CacheNames { + + String NOTICE_ONE = "blade:notice:one"; + +} diff --git a/blade-biz-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java b/blade-biz-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java new file mode 100644 index 0000000..d3a2164 --- /dev/null +++ b/blade-biz-common/src/main/java/org/springblade/common/config/BladeCommonConfiguration.java @@ -0,0 +1,41 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.common.config; + + +import lombok.AllArgsConstructor; +import org.springframework.context.annotation.Configuration; + +/** + * 公共封装包配置类 + * + * @author Chill + */ +@Configuration(proxyBeanMethods = false) +@AllArgsConstructor +public class BladeCommonConfiguration { + +} diff --git a/blade-biz-common/src/main/java/org/springblade/common/constant/CommonConstant.java b/blade-biz-common/src/main/java/org/springblade/common/constant/CommonConstant.java new file mode 100644 index 0000000..3afb3eb --- /dev/null +++ b/blade-biz-common/src/main/java/org/springblade/common/constant/CommonConstant.java @@ -0,0 +1,61 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.common.constant; + +/** + * 通用常量 + * + * @author Chill + */ +public interface CommonConstant { + + /** + * sword 系统名 + */ + String SWORD_NAME = "sword"; + + /** + * saber 系统名 + */ + String SABER_NAME = "saber"; + + /** + * 顶级父节点id + */ + Integer TOP_PARENT_ID = 0; + + /** + * 顶级父节点名称 + */ + String TOP_PARENT_NAME = "顶级"; + + + /** + * 默认密码 + */ + String DEFAULT_PASSWORD = "123456"; + +} diff --git a/blade-biz-common/src/main/java/org/springblade/common/constant/LauncherConstant.java b/blade-biz-common/src/main/java/org/springblade/common/constant/LauncherConstant.java new file mode 100644 index 0000000..3c470e7 --- /dev/null +++ b/blade-biz-common/src/main/java/org/springblade/common/constant/LauncherConstant.java @@ -0,0 +1,216 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 { + + /** + * nacos 用户名 + */ + String NACOS_USERNAME = "nacos"; + + /** + * nacos 密码 + */ + String NACOS_PASSWORD = "nacos"; + + /** + * xxljob + */ + String APPLICATION_XXLJOB_NAME = APPLICATION_NAME_PREFIX + "xxljob"; + + /** + * xxljob + */ + String APPLICATION_XXLJOB_ADMIN_NAME = APPLICATION_NAME_PREFIX + "xxljob-admin"; + + /** + * nacos namespace id + */ + String NACOS_NAMESPACE = "f447a694-519a-4255-95f9-bcbb5a5d6369"; + + /** + * nacos dev 地址 + */ + String NACOS_DEV_ADDR = "127.0.0.1:8848"; + + /** + * nacos prod 地址 + */ + String NACOS_PROD_ADDR = "172.30.0.48:8848"; + + /** + * nacos test 地址 + */ + String NACOS_TEST_ADDR = "172.19.2.126:30848"; + + /** + * 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"; + + /** + * sharding + */ + String APPLICATION_SHARDING_NAME = APPLICATION_NAME_PREFIX + "sharding"; + + /** + * seata订单 + */ + String APPLICATION_SEATA_ORDER_NAME = APPLICATION_NAME_PREFIX + "seata-order"; + + /** + * seata库存 + */ + String APPLICATION_SEATA_STORAGE_NAME = APPLICATION_NAME_PREFIX + "seata-storage"; + + /** + * mqtt-broker + */ + String APPLICATION_MQTT_BROKER_NAME = APPLICATION_NAME_PREFIX + "mqtt-broker"; + + /** + * mqtt-client + */ + String APPLICATION_MQTT_CLIENT_NAME = APPLICATION_NAME_PREFIX + "mqtt-client"; + + /** + * rabbit-listener + */ + String APPLICATION_RABBIT_LISTENER_NAME = APPLICATION_NAME_PREFIX + "rabbit-listener"; + + /** + * rabbit-publisher + */ + String APPLICATION_RABBIT_PUBLISHER_NAME = APPLICATION_NAME_PREFIX + "rabbit-publisher"; + + /** + * seata file模式 + */ + String FILE_MODE = "file"; + + /** + * seata nacos模式 + */ + String NACOS_MODE = "nacos"; + + /** + * seata default模式 + */ + String DEFAULT_MODE = "default"; + + /** + * seata group后缀 + */ + String GROUP_NAME = "-group"; + + /** + * 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) { + return switch (profile) { + case (AppConstant.PROD_CODE) -> NACOS_PROD_ADDR; + case (AppConstant.TEST_CODE) -> NACOS_TEST_ADDR; + default -> NACOS_DEV_ADDR; + }; + } + + /** + * 动态获取sentinel地址 + * + * @param profile 环境变量 + * @return addr + */ + static String sentinelAddr(String profile) { + return switch (profile) { + case (AppConstant.PROD_CODE) -> SENTINEL_PROD_ADDR; + case (AppConstant.TEST_CODE) -> SENTINEL_TEST_ADDR; + default -> SENTINEL_DEV_ADDR; + }; + } + + /** + * 动态获取seata地址 + * + * @param profile 环境变量 + * @return addr + */ + static String seataAddr(String profile) { + return switch (profile) { + case (AppConstant.PROD_CODE) -> SEATA_PROD_ADDR; + case (AppConstant.TEST_CODE) -> SEATA_TEST_ADDR; + default -> SEATA_DEV_ADDR; + }; + } + +} diff --git a/blade-biz-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java b/blade-biz-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java new file mode 100644 index 0000000..ba14827 --- /dev/null +++ b/blade-biz-common/src/main/java/org/springblade/common/launch/LauncherServiceImpl.java @@ -0,0 +1,75 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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(); + + // nacos注册中心配置 + PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.username", LauncherConstant.NACOS_USERNAME); + PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.password", LauncherConstant.NACOS_PASSWORD); + PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", LauncherConstant.nacosAddr(profile)); + // nacos配置中心配置 + PropsUtil.setProperty(props, "spring.cloud.nacos.config.username", LauncherConstant.NACOS_USERNAME); + PropsUtil.setProperty(props, "spring.cloud.nacos.config.password", LauncherConstant.NACOS_PASSWORD); + PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", LauncherConstant.nacosAddr(profile)); + // sentinel配置 + PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.sentinelAddr(profile)); + // 多数据源配置 + PropsUtil.setProperty(props, "spring.datasource.dynamic.enabled", "false"); + + // 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)); + + } + +} diff --git a/blade-biz-common/src/main/java/org/springblade/common/utils/CommonUtil.java b/blade-biz-common/src/main/java/org/springblade/common/utils/CommonUtil.java new file mode 100644 index 0000000..28a8202 --- /dev/null +++ b/blade-biz-common/src/main/java/org/springblade/common/utils/CommonUtil.java @@ -0,0 +1,35 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.common.utils; + +/** + * 通用工具类 + * + * @author Chill + */ +public class CommonUtil { + +} diff --git a/blade-biz-common/src/main/resources/banner.txt b/blade-biz-common/src/main/resources/banner.txt new file mode 100644 index 0000000..9cc3783 --- /dev/null +++ b/blade-biz-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 :: ${spring.application.name}:${AnsiColor.RED}${blade.env}${AnsiColor.BLUE} :: Running SpringBoot ${spring-boot.version} :: ${AnsiColor.BRIGHT_BLACK} diff --git a/blade-example/blade-apollo/doc/apollo/blade-apollo-dev.yaml b/blade-example/blade-apollo/doc/apollo/blade-apollo-dev.yaml new file mode 100644 index 0000000..06a4cab --- /dev/null +++ b/blade-example/blade-apollo/doc/apollo/blade-apollo-dev.yaml @@ -0,0 +1,3 @@ +#自定义配置 +apollo: + name: apollo-name diff --git a/blade-example/blade-apollo/pom.xml b/blade-example/blade-apollo/pom.xml new file mode 100644 index 0000000..bdb8470 --- /dev/null +++ b/blade-example/blade-apollo/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + org.springblade + blade-example + ${revision} + + + blade-apollo + + + + + org.springblade + blade-biz-common + + + org.springblade + blade-core-cloud + + + org.springblade + blade-core-launch + + + com.ctrip.framework.apollo + apollo-client + 2.2.0 + + + + + + + io.fabric8 + docker-maven-plugin + + ${docker.fabric.skip} + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + diff --git a/blade-example/blade-apollo/src/main/java/org/springblade/ApolloApplication.java b/blade-example/blade-apollo/src/main/java/org/springblade/ApolloApplication.java new file mode 100644 index 0000000..624c8f2 --- /dev/null +++ b/blade-example/blade-apollo/src/main/java/org/springblade/ApolloApplication.java @@ -0,0 +1,46 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.cloud.client.BladeCloudApplication; +import org.springblade.core.launch.BladeApplication; + +/** + * Apollo启动器 + * + * @author Chill + */ +@Slf4j +@BladeCloudApplication +public class ApolloApplication { + + public static void main(String[] args) { + BladeApplication.run("blade-apollo", ApolloApplication.class, args); + } + +} + diff --git a/blade-example/blade-apollo/src/main/java/org/springblade/listener/ApolloListener.java b/blade-example/blade-apollo/src/main/java/org/springblade/listener/ApolloListener.java new file mode 100644 index 0000000..7d51bab --- /dev/null +++ b/blade-example/blade-apollo/src/main/java/org/springblade/listener/ApolloListener.java @@ -0,0 +1,52 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * ApolloListener + * + * @author Chill + */ +@Slf4j +@Component +public class ApolloListener { + + @Value("${apollo.name:undefined}") + private String name; + + @EventListener(ApplicationReadyEvent.class) + public void handleApplicationReady() { + log.info("====================ApolloListener ready to work===================="); + log.info("apollo.name:{}", name); + log.info("====================ApolloListener ready to work===================="); + } +} diff --git a/blade-example/blade-apollo/src/main/resources/application.yml b/blade-example/blade-apollo/src/main/resources/application.yml new file mode 100644 index 0000000..7468743 --- /dev/null +++ b/blade-example/blade-apollo/src/main/resources/application.yml @@ -0,0 +1,13 @@ +#服务器端口 +server: + port: 8508 + +app: + id: blade-apollo +apollo: + #apollo官方文档:https://www.apolloconfig.com/ + #apollo官方测试地址:http://81.68.181.139 ,账号/密码:apollo/admin + meta: http://81.68.181.139:8080 + bootstrap: + enabled: true + namespaces: application,common diff --git a/blade-example/blade-mqtt-broker/pom.xml b/blade-example/blade-mqtt-broker/pom.xml new file mode 100644 index 0000000..a76201e --- /dev/null +++ b/blade-example/blade-mqtt-broker/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.springblade + blade-example + ${revision} + + + blade-mqtt-broker + ${project.artifactId} + jar + + + + net.dreamlu + mica-mqttx-server-spring-boot-starter + 3.1.12 + + + + diff --git a/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/MqttBrokerApplication.java b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/MqttBrokerApplication.java new file mode 100644 index 0000000..4e1124f --- /dev/null +++ b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/MqttBrokerApplication.java @@ -0,0 +1,47 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.broker; + +import org.springblade.core.launch.BladeApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +import static org.springblade.common.constant.LauncherConstant.APPLICATION_MQTT_BROKER_NAME; + +/** + * MQTT Broker 启动器 + * + * @author Chill + */ +@EnableScheduling +@SpringBootApplication +public class MqttBrokerApplication { + + public static void main(String[] args) { + BladeApplication.run(APPLICATION_MQTT_BROKER_NAME, MqttBrokerApplication.class, args); + } + +} diff --git a/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttConnectStatusListener.java b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttConnectStatusListener.java new file mode 100644 index 0000000..7184c45 --- /dev/null +++ b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttConnectStatusListener.java @@ -0,0 +1,107 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.broker.listener; + +import lombok.extern.slf4j.Slf4j; +import net.dreamlu.iot.mqtt.spring.server.event.MqttClientOfflineEvent; +import net.dreamlu.iot.mqtt.spring.server.event.MqttClientOnlineEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +/** + * 客户端上下线监听器 + * + * @author Chill + */ +@Slf4j +@Service +public class MqttConnectStatusListener { + + @EventListener + public void online(MqttClientOnlineEvent event) { + log.info("MQTT客户端上线: {}", event); + + // 客户端上线处理逻辑 + String clientId = event.getClientId(); + + log.info("客户端上线 - clientId: {}", clientId); + + // 可以在这里添加上线处理逻辑,例如: + // 1. 记录上线日志 + // 2. 更新设备状态 + // 3. 发送上线通知 + // 4. 统计在线数量等 + + handleClientOnline(clientId); + } + + @EventListener + public void offline(MqttClientOfflineEvent event) { + log.info("MQTT客户端下线: {}", event); + + // 客户端下线处理逻辑 + String clientId = event.getClientId(); + + log.info("客户端下线 - clientId: {}", clientId); + + // 可以在这里添加下线处理逻辑,例如: + // 1. 记录下线日志 + // 2. 更新设备状态 + // 3. 发送下线通知 + // 4. 清理资源等 + + handleClientOffline(clientId); + } + + /** + * 处理客户端上线 + * + * @param clientId 客户端ID + */ + private void handleClientOnline(String clientId) { + try { + // 添加具体的上线处理逻辑 + log.debug("执行客户端上线处理: clientId={}", clientId); + } catch (Exception e) { + log.error("处理客户端上线异常: clientId={}", clientId, e); + } + } + + /** + * 处理客户端下线 + * + * @param clientId 客户端ID + */ + private void handleClientOffline(String clientId) { + try { + // 添加具体的下线处理逻辑 + log.debug("执行客户端下线处理: clientId={}", clientId); + } catch (Exception e) { + log.error("处理客户端下线异常: clientId={}", clientId, e); + } + } + +} diff --git a/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttServerMessageListener.java b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttServerMessageListener.java new file mode 100644 index 0000000..80bf6a6 --- /dev/null +++ b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/listener/MqttServerMessageListener.java @@ -0,0 +1,91 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.broker.listener; + +import lombok.extern.slf4j.Slf4j; +import net.dreamlu.iot.mqtt.codec.MqttPublishMessage; +import net.dreamlu.iot.mqtt.codec.MqttQoS; +import net.dreamlu.iot.mqtt.core.server.event.IMqttMessageListener; +import org.springframework.stereotype.Service; +import org.tio.core.ChannelContext; +import org.tio.utils.buffer.ByteBufferUtil; + +/** + * 服务端接收消息监听器 + * + * @author Chill + */ +@Slf4j +@Service +public class MqttServerMessageListener implements IMqttMessageListener { + + @Override + public void onMessage(ChannelContext context, String clientId, String topic, MqttQoS qoS, MqttPublishMessage message) { + log.info("收到MQTT消息 - clientId: {}, topic: {}, qoS: {}, payload: {}", + clientId, topic, qoS, ByteBufferUtil.toString(message.getPayload())); + + // 自定义数据流转逻辑 + // 可以在这里添加消息处理逻辑,例如: + // 1. 数据解析和格式化 + // 2. 业务规则处理 + // 3. 数据存储 + // 4. 消息转发 + // 5. 告警处理等 + + handleMessage(clientId, topic, message); + } + + /** + * 处理接收到的消息 + * + * @param clientId 客户端ID + * @param topic 主题 + * @param message 消息 + */ + private void handleMessage(String clientId, String topic, MqttPublishMessage message) { + try { + String payload = ByteBufferUtil.toString(message.getPayload()); + + // 根据主题进行不同的处理 + if (topic.contains("/property/")) { + log.info("处理属性消息: {}", payload); + // 处理设备属性上报 + } else if (topic.contains("/event/")) { + log.info("处理事件消息: {}", payload); + // 处理设备事件上报 + } else if (topic.contains("/command/")) { + log.info("处理命令消息: {}", payload); + // 处理设备命令响应 + } else { + log.info("处理通用消息: {}", payload); + // 处理其他类型消息 + } + + } catch (Exception e) { + log.error("处理MQTT消息异常: clientId={}, topic={}", clientId, topic, e); + } + } +} diff --git a/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/IMqttAuthService.java b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/IMqttAuthService.java new file mode 100644 index 0000000..52d94bf --- /dev/null +++ b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/IMqttAuthService.java @@ -0,0 +1,49 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.broker.service; + +import org.tio.core.ChannelContext; + +/** + * MQTT 认证服务 + * + * @author Chill + */ +public interface IMqttAuthService { + + /** + * 设备连接认证 + * + * @param context ChannelContext + * @param clientId clientId + * @param userName userName + * @param password password + * @param clientNodeIp clientNodeIp + * @return 是否成功 + */ + boolean hasAuth(ChannelContext context, String clientId, String userName, String password, String clientNodeIp); + +} diff --git a/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/impl/MqttAuthServiceImpl.java b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/impl/MqttAuthServiceImpl.java new file mode 100644 index 0000000..cb85588 --- /dev/null +++ b/blade-example/blade-mqtt-broker/src/main/java/org/springblade/mqtt/broker/service/impl/MqttAuthServiceImpl.java @@ -0,0 +1,67 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.broker.service.impl; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.mqtt.broker.service.IMqttAuthService; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.tio.core.ChannelContext; + +/** + * MQTT 认证处理服务 + * + * @author Chill + */ +@Slf4j +@Service +public class MqttAuthServiceImpl implements IMqttAuthService { + + /** + * MQTT 认证最大间隔时间,默认 5 分钟 + */ + @Value("${mqtt.auth.timeSecondDiff:300}") + private int timeSecondDiff; + + @Override + public boolean hasAuth(ChannelContext context, String clientId, String userName, String password, String clientNodeIp) { + log.info("MQTT 客户端认证: clientId={}, userName={}, clientNodeIp={}", clientId, userName, clientNodeIp); + + // 自定义账号认证逻辑 + // 这里可以根据实际需求添加具体的认证逻辑 + // 例如:查询数据库验证用户名密码、检查设备白名单等 + + // 简单的demo认证逻辑:允许所有连接 + if (userName != null && password != null) { + log.info("MQTT 客户端认证成功: clientId={}", clientId); + return true; + } + + log.warn("MQTT 客户端认证失败: clientId={}", clientId); + return false; + } + +} diff --git a/blade-example/blade-mqtt-broker/src/main/resources/application.yml b/blade-example/blade-mqtt-broker/src/main/resources/application.yml new file mode 100644 index 0000000..3ab0559 --- /dev/null +++ b/blade-example/blade-mqtt-broker/src/main/resources/application.yml @@ -0,0 +1,45 @@ +# 服务器配置 +server: + port: 8290 + undertow: + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + buffer-size: 1024 + # 是否分配的直接内存 + direct-buffers: true + # 线程配置 + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 16 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 400 + servlet: + # 编码配置 + encoding: + charset: UTF-8 + force: true + +# MQTT服务端配置 +mqtt: + server: + enabled: true # 是否开启服务端,默认:true + port: 11883 # 端口,默认:1883 + name: Blade-MQTT-Broker # 名称,默认:Mica-Mqtt-Server + heartbeat-timeout: 120000 # 心跳超时,单位毫秒,默认: 1000 * 120 + read-buffer-size: 8KB # 接收数据的 buffer size,默认:8k + max-bytes-in-message: 10MB # 消息解析最大 bytes 长度,默认:10M + auth: + enable: false # 是否开启 mqtt 认证 + debug: true # 如果开启 prometheus 指标收集建议关闭 + stat-enable: true # 开启指标收集,debug 和 prometheus 开启时需要打开,默认开启,关闭节省内存 + web-port: 28083 # http、websocket 端口,默认:8083 + websocket-enable: true # 是否开启 websocket,默认: true + http-enable: false # 是否开启 http api,默认: false + ssl: # mqtt tcp ssl 认证 + enabled: false # 是否开启 ssl 认证,2.1.0 开始支持双向认证 + keystore-path: # 必须参数:ssl keystore 目录,支持 classpath:/ 路径。 + keystore-pass: # 必选参数:ssl keystore 密码 + truststore-path: # 可选参数:ssl 双向认证 truststore 目录,支持 classpath:/ 路径。 + truststore-pass: # 可选参数:ssl 双向认证 truststore 密码 + client-auth: none # 是否需要客户端认证(双向认证),默认:NONE(不需要) + auth: + timeSecondDiff: 300 # 认证时间差,默认5分钟 diff --git a/blade-example/blade-mqtt-client/pom.xml b/blade-example/blade-mqtt-client/pom.xml new file mode 100644 index 0000000..4088f28 --- /dev/null +++ b/blade-example/blade-mqtt-client/pom.xml @@ -0,0 +1,24 @@ + + + 4.0.0 + + org.springblade + blade-example + ${revision} + + + blade-mqtt-client + ${project.artifactId} + jar + + + + net.dreamlu + mica-mqttx-client + 3.1.12 + + + + diff --git a/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/MqttClientApplication.java b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/MqttClientApplication.java new file mode 100644 index 0000000..b40306d --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/MqttClientApplication.java @@ -0,0 +1,47 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.client; + +import org.springblade.core.launch.BladeApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +import static org.springblade.common.constant.LauncherConstant.APPLICATION_MQTT_CLIENT_NAME; + +/** + * MQTT Client 启动器 + * + * @author Chill + */ +@EnableScheduling +@SpringBootApplication +public class MqttClientApplication { + + public static void main(String[] args) { + BladeApplication.run(APPLICATION_MQTT_CLIENT_NAME, MqttClientApplication.class, args); + } + +} diff --git a/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/listener/DeviceListener.java b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/listener/DeviceListener.java new file mode 100644 index 0000000..870db84 --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/listener/DeviceListener.java @@ -0,0 +1,98 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.client.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.mqtt.client.simulator.DeviceSimulator; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +/** + * 设备应用监听器 + * + * @author Chill + */ +@Slf4j +@Component +public class DeviceListener { + + @Value("${mqtt.client.productKey:DEMO_PRODUCT}") + private String productKey; + + @Value("${mqtt.client.deviceName:DEMO_DEVICE}") + private String deviceName; + + @Value("${mqtt.client.deviceSecret:demo_secret_123}") + private String deviceSecret; + + @Value("${mqtt.client.serverHost:localhost}") + private String serverHost; + + @Value("${mqtt.client.serverPort:1883}") + private int serverPort; + + @Value("${mqtt.client.enabled:true}") + private boolean enabled; + + @EventListener(ApplicationReadyEvent.class) + public void handleApplicationReady() { + if (!enabled) { + log.info("MQTT客户端已禁用,跳过启动"); + return; + } + + log.info("应用启动完成,开始初始化MQTT客户端"); + log.info("配置信息: productKey={}, deviceName={}, serverHost={}:{}", + productKey, deviceName, serverHost, serverPort); + + try { + // 初始化设备模拟器 + DeviceSimulator simulator = new DeviceSimulator( + productKey, + deviceName, + deviceSecret, + serverHost, + serverPort + ); + + // 启动模拟器 + simulator.start(); + + log.info("设备模拟器启动成功"); + + // 添加关闭钩子 + Runtime.getRuntime().addShutdownHook(new Thread(() -> { + log.info("应用关闭,停止设备模拟器"); + simulator.stop(); + })); + + } catch (Exception e) { + log.error("启动设备模拟器失败", e); + } + } +} diff --git a/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/simulator/DeviceSimulator.java b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/simulator/DeviceSimulator.java new file mode 100644 index 0000000..f7c73dc --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/simulator/DeviceSimulator.java @@ -0,0 +1,282 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.client.simulator; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.dreamlu.iot.mqtt.core.client.MqttClient; +import org.springblade.mqtt.client.support.DataReq; +import org.springblade.mqtt.client.support.NtpReq; +import org.tio.utils.buffer.ByteBufferUtil; +import org.tio.utils.json.JsonUtil; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * 设备模拟器 + * + * @author Chill + */ +@Slf4j +@RequiredArgsConstructor +public class DeviceSimulator { + private final String productKey; + private final String deviceName; + private final String deviceSecret; + private final String serverHost; + private final int serverPort; + + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4); + + /** + * 启动模拟器 + */ + public void start() { + log.info("启动设备模拟器: productKey={}, deviceName={}", productKey, deviceName); + + // 初始化 MQTT 客户端 + String clientId = generateClientId(); + String username = generateUsername(); + String password = generatePassword(); + + log.info("连接配置: clientId={}, username={}, host={}:{}", clientId, username, serverHost, serverPort); + + try { + MqttClient client = MqttClient.create() + .ip(serverHost) + .port(serverPort) + .username(username) + .password(password) + .clientId(clientId) + .connectSync(); + + log.info("MQTT客户端连接成功"); + + // 订阅主题 + subscribeTopics(client); + // 启动任务调度 + scheduleTasks(client); + + } catch (Exception e) { + log.error("MQTT客户端连接失败", e); + } + } + + /** + * 生成客户端ID + * + * @return clientId + */ + private String generateClientId() { + return productKey + "_" + deviceName + "_" + System.currentTimeMillis(); + } + + /** + * 生成用户名 + * + * @return username + */ + private String generateUsername() { + return deviceName + "&" + productKey; + } + + /** + * 生成密码 + * + * @return password + */ + private String generatePassword() { + // 简化的密码生成逻辑 + return deviceSecret; + } + + /** + * 订阅主题 + * + * @param client MqttClient + */ + private void subscribeTopics(MqttClient client) { + // 订阅设备相关主题 + String topicPrefix = "/blade/sys/" + productKey + "/" + deviceName; + + // 订阅属性设置 + client.subQos0(topicPrefix + "/thing/service/property/set", (context, topic, message, payload) -> { + log.info("收到属性设置: topic={}, payload={}", topic, ByteBufferUtil.toString(payload)); + }); + + // 订阅服务调用 + client.subQos0(topicPrefix + "/thing/service/+", (context, topic, message, payload) -> { + log.info("收到服务调用: topic={}, payload={}", topic, ByteBufferUtil.toString(payload)); + }); + + // 订阅NTP响应 + client.subQos0("/blade/ext/ntp/" + productKey + "/" + deviceName + "/response", (context, topic, message, payload) -> { + log.info("收到NTP响应: topic={}, payload={}", topic, ByteBufferUtil.toString(payload)); + }); + + log.info("已订阅设备主题: {}", topicPrefix); + } + + /** + * 任务调度 + * + * @param client MqttClient + */ + private void scheduleTasks(MqttClient client) { + // 定期上报属性数据(每30秒) + scheduler.scheduleAtFixedRate(() -> { + publishPropertyData(client); + }, 5, 30, TimeUnit.SECONDS); + + // 定期上报事件数据(每60秒) + scheduler.scheduleAtFixedRate(() -> { + publishEventData(client); + }, 10, 60, TimeUnit.SECONDS); + + // 定期发送心跳数据(每120秒) + scheduler.scheduleAtFixedRate(() -> { + publishHeartbeat(client); + }, 15, 120, TimeUnit.SECONDS); + + // 定期请求NTP时间(每300秒) + scheduler.scheduleAtFixedRate(() -> { + publishNtpRequest(client); + }, 20, 300, TimeUnit.SECONDS); + + log.info("已启动定时任务调度"); + } + + /** + * 发布属性数据 + * + * @param client MqttClient + */ + private void publishPropertyData(MqttClient client) { + try { + DataReq> req = new DataReq<>(); + req.setId(UUID.randomUUID().toString().replace("-", "")); + req.setVersion("1.0"); + + Map params = new HashMap<>(); + params.put("temperature", 20 + Math.random() * 10); // 温度:20-30度 + params.put("humidity", 40 + Math.random() * 30); // 湿度:40-70% + params.put("lightSwitch", Math.random() > 0.5 ? 1 : 0); // 灯开关 + req.setParams(params); + + String topic = "/blade/sys/" + productKey + "/" + deviceName + "/thing/event/property/post"; + client.publish(topic, JsonUtil.toJsonBytes(req)); + + log.info("已发布属性数据: {}", JsonUtil.toJsonString(params)); + } catch (Exception e) { + log.error("发布属性数据失败", e); + } + } + + /** + * 发布事件数据 + * + * @param client MqttClient + */ + private void publishEventData(MqttClient client) { + try { + DataReq> req = new DataReq<>(); + req.setId(UUID.randomUUID().toString().replace("-", "")); + req.setVersion("1.0"); + + Map params = new HashMap<>(); + Map output = new HashMap<>(); + output.put("eventLevel", "INFO"); + output.put("eventMessage", "设备运行正常"); + output.put("timestamp", System.currentTimeMillis()); + + params.put("output", JsonUtil.toJsonString(output)); + params.put("eventName", "状态事件"); + params.put("eventType", "info"); + req.setParams(params); + + String topic = "/blade/sys/" + productKey + "/" + deviceName + "/thing/event/StatusEvent/post"; + client.publish(topic, JsonUtil.toJsonBytes(req)); + + log.info("已发布事件数据: {}", JsonUtil.toJsonString(params)); + } catch (Exception e) { + log.error("发布事件数据失败", e); + } + } + + /** + * 发布心跳数据 + * + * @param client MqttClient + */ + private void publishHeartbeat(MqttClient client) { + try { + Map heartbeat = new HashMap<>(); + heartbeat.put("deviceId", deviceName); + heartbeat.put("productKey", productKey); + heartbeat.put("timestamp", System.currentTimeMillis()); + heartbeat.put("status", "online"); + + String topic = "/blade/sys/" + productKey + "/" + deviceName + "/heartbeat"; + client.publish(topic, JsonUtil.toJsonBytes(heartbeat)); + + log.info("已发布心跳数据: {}", JsonUtil.toJsonString(heartbeat)); + } catch (Exception e) { + log.error("发布心跳数据失败", e); + } + } + + /** + * 发布NTP请求 + * + * @param client MqttClient + */ + private void publishNtpRequest(MqttClient client) { + try { + NtpReq req = new NtpReq(); + req.setDeviceSendTime(String.valueOf(System.currentTimeMillis())); + + String topic = "/blade/ext/ntp/" + productKey + "/" + deviceName + "/request"; + client.publish(topic, JsonUtil.toJsonBytes(req)); + + log.info("已发布NTP请求: {}", JsonUtil.toJsonString(req)); + } catch (Exception e) { + log.error("发布NTP请求失败", e); + } + } + + /** + * 停止模拟器 + */ + public void stop() { + scheduler.shutdown(); + log.info("设备模拟器已停止"); + } +} diff --git a/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/DataReq.java b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/DataReq.java new file mode 100644 index 0000000..62b263f --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/DataReq.java @@ -0,0 +1,67 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.client.support; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.io.Serializable; + +/** + * 设备通用数据格式 + * + * @author Chill + */ +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class DataReq implements Serializable { + + /** + * 消息ID号。uuid,去掉短横线,32位,全局唯一,用于ack或系统消息追踪 + */ + private String id; + + /** + * 协议版本号,目前协议版本号唯一取值为1.0 + */ + private String version; + + /** + * 扩展功能的参数,其下包含各功能字段。平台可扩展,或可自行扩展,自行扩展的参数需在自定义解析模块自行解析 + */ + private SysBean sys; + + /** + * 请求方法。 + */ + private String method; + + /** + * 请求参数 + */ + private T params; + +} diff --git a/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/NtpReq.java b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/NtpReq.java new file mode 100644 index 0000000..ca94da8 --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/NtpReq.java @@ -0,0 +1,45 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.client.support; + +import lombok.Data; + +import java.io.Serializable; + +/** + * NTP 请求 + * + * @author Chill + */ +@Data +public class NtpReq implements Serializable { + + /** + * 设备端发送时间 + */ + private String deviceSendTime; + +} diff --git a/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/SysBean.java b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/SysBean.java new file mode 100644 index 0000000..3ca50e8 --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/java/org/springblade/mqtt/client/support/SysBean.java @@ -0,0 +1,62 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.mqtt.client.support; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 扩展功能 + * + * @author Chill + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SysBean implements Serializable { + + /** + * 不响应 ack + */ + public static final int ACK_NO = 0; + /** + * 响应 ack + */ + public static final int ACK_NEED = 1; + + public SysBean(boolean ackNeed) { + this(ackNeed ? ACK_NEED : ACK_NO); + } + + /** + * 扩展功能字段,表示是否返回响应数据。0:不返回响应数据1:返回响应数据 + */ + private int ack; + +} diff --git a/blade-example/blade-mqtt-client/src/main/resources/application.yml b/blade-example/blade-mqtt-client/src/main/resources/application.yml new file mode 100644 index 0000000..0e46e96 --- /dev/null +++ b/blade-example/blade-mqtt-client/src/main/resources/application.yml @@ -0,0 +1,29 @@ +# 服务器配置 +server: + port: 8291 + undertow: + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + buffer-size: 1024 + # 是否分配的直接内存 + direct-buffers: true + # 线程配置 + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 16 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 400 + servlet: + # 编码配置 + encoding: + charset: UTF-8 + force: true + +# MQTT客户端配置 +mqtt: + client: + enabled: true # 是否启用MQTT客户端 + productKey: "BLADE_DEMO_PRODUCT" # 产品标识 + deviceName: "BLADE_DEMO_DEVICE_001" # 设备名称 + deviceSecret: "blade_demo_secret_123456" # 设备密钥 + serverHost: "localhost" # MQTT服务器地址 + serverPort: 11883 # MQTT服务器端口 diff --git a/blade-example/blade-rabbit-listener/pom.xml b/blade-example/blade-rabbit-listener/pom.xml new file mode 100644 index 0000000..5cdf699 --- /dev/null +++ b/blade-example/blade-rabbit-listener/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.springblade + blade-example + ${revision} + + + blade-rabbit-listener + ${project.artifactId} + jar + + + + org.springblade + blade-core-tool + + + org.springframework.boot + spring-boot-starter-amqp + + + + diff --git a/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/RabbitListenerApplication.java b/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/RabbitListenerApplication.java new file mode 100644 index 0000000..50f355f --- /dev/null +++ b/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/RabbitListenerApplication.java @@ -0,0 +1,47 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.listener; + +import org.springblade.core.launch.BladeApplication; +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +import static org.springblade.common.constant.LauncherConstant.APPLICATION_RABBIT_LISTENER_NAME; + +/** + * Rabbit 消费者启动器 + * + * @author Chill + */ +@EnableRabbit +@SpringBootApplication +public class RabbitListenerApplication { + + public static void main(String[] args) { + BladeApplication.run(APPLICATION_RABBIT_LISTENER_NAME, RabbitListenerApplication.class, args); + } + +} diff --git a/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/config/RabbitListenerConfiguration.java b/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/config/RabbitListenerConfiguration.java new file mode 100644 index 0000000..22db3a7 --- /dev/null +++ b/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/config/RabbitListenerConfiguration.java @@ -0,0 +1,92 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.listener.config; + +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.Queue; +import org.springframework.amqp.core.TopicExchange; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Rabbit 消费者配置 + * + * @author Chill + */ +@Configuration +public class RabbitListenerConfiguration { + + /** + * 示例队列名 + */ + public static final String DEMO_QUEUE_NAME = "blade.demo.queue"; + + /** + * 消息队列名 + */ + public static final String MESSAGE_QUEUE_NAME = "blade.message.queue"; + + /** + * 交换机名 + */ + public static final String EXCHANGE_NAME = "blade.amqp.exchange"; + + /** + * 示例路由规则 + */ + public static final String DEMO_ROUTING_KEY_PATTERN = "demo.#"; + + /** + * 消息路由规则 + */ + public static final String MESSAGE_ROUTING_KEY_PATTERN = "message.#"; + + @Bean + public TopicExchange exchange() { + return new TopicExchange(EXCHANGE_NAME); + } + + @Bean + public Queue demoQueue() { + return new Queue(DEMO_QUEUE_NAME); + } + + @Bean + public Queue messageQueue() { + return new Queue(MESSAGE_QUEUE_NAME); + } + + @Bean + public Binding demoBinding(Queue demoQueue, TopicExchange exchange) { + return BindingBuilder.bind(demoQueue).to(exchange).with(DEMO_ROUTING_KEY_PATTERN); + } + + @Bean + public Binding messageBinding(Queue messageQueue, TopicExchange exchange) { + return BindingBuilder.bind(messageQueue).to(exchange).with(MESSAGE_ROUTING_KEY_PATTERN); + } +} diff --git a/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/listener/MessageConsumer.java b/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/listener/MessageConsumer.java new file mode 100644 index 0000000..036a51f --- /dev/null +++ b/blade-example/blade-rabbit-listener/src/main/java/org/springblade/rabbit/listener/listener/MessageConsumer.java @@ -0,0 +1,116 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.listener.listener; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.rabbit.listener.config.RabbitListenerConfiguration; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.stereotype.Component; + +/** + * Rabbit 消息消费者 + * + * @author Chill + */ +@Slf4j +@Component +public class MessageConsumer { + + /** + * 监听示例队列消息 + * + * @param message 消息 + */ + @RabbitListener(queues = RabbitListenerConfiguration.DEMO_QUEUE_NAME) + public void receiveDemoMessage(Message message) { + String routingKey = message.getMessageProperties().getReceivedRoutingKey(); + String payload = new String(message.getBody()); + log.info("收到示例消息: routingKey={}, payload={}", routingKey, payload); + + // 处理示例消息的业务逻辑 + processDemoMessage(routingKey, payload); + } + + /** + * 监听普通消息队列消息 + * + * @param message 消息 + */ + @RabbitListener(queues = RabbitListenerConfiguration.MESSAGE_QUEUE_NAME) + public void receiveMessage(Message message) { + String routingKey = message.getMessageProperties().getReceivedRoutingKey(); + String payload = new String(message.getBody()); + log.info("收到普通消息: routingKey={}, payload={}", routingKey, payload); + + // 处理普通消息的业务逻辑 + processMessage(routingKey, payload); + } + + /** + * 处理示例消息 + * + * @param routingKey 路由键 + * @param payload 消息载荷 + */ + private void processDemoMessage(String routingKey, String payload) { + try { + // 这里可以添加具体的业务处理逻辑 + log.debug("处理示例消息: routingKey={}, payload={}", routingKey, payload); + + // 示例: 如果是特定的路由键,执行特定操作 + if (routingKey.startsWith("demo.test")) { + log.info("执行测试操作: {}", payload); + } else if (routingKey.startsWith("demo.data")) { + log.info("处理数据: {}", payload); + } + } catch (Exception e) { + log.error("处理示例消息失败: routingKey={}, payload={}", routingKey, payload, e); + } + } + + /** + * 处理普通消息 + * + * @param routingKey 路由键 + * @param payload 消息载荷 + */ + private void processMessage(String routingKey, String payload) { + try { + // 这里可以添加具体的业务处理逻辑 + log.debug("处理普通消息: routingKey={}, payload={}", routingKey, payload); + + // 示例: 根据路由键进行不同的处理 + if (routingKey.startsWith("message.notify")) { + log.info("处理通知消息: {}", payload); + } else if (routingKey.startsWith("message.event")) { + log.info("处理事件消息: {}", payload); + } + } catch (Exception e) { + log.error("处理普通消息失败: routingKey={}, payload={}", routingKey, payload, e); + } + } +} diff --git a/blade-example/blade-rabbit-listener/src/main/resources/application.yml b/blade-example/blade-rabbit-listener/src/main/resources/application.yml new file mode 100644 index 0000000..57d91f6 --- /dev/null +++ b/blade-example/blade-rabbit-listener/src/main/resources/application.yml @@ -0,0 +1,22 @@ +server: + port: 8302 + +spring: + # RabbitMQ 配置 + rabbitmq: + host: localhost + port: 5672 + username: root + password: root + virtual-host: / + connection-timeout: 15000 + # 消费者配置 + listener: + simple: + acknowledge-mode: auto + retry: + enabled: true + initial-interval: 1000 + max-attempts: 3 + max-interval: 10000 + multiplier: 1.0 \ No newline at end of file diff --git a/blade-example/blade-rabbit-publisher/pom.xml b/blade-example/blade-rabbit-publisher/pom.xml new file mode 100644 index 0000000..178cc67 --- /dev/null +++ b/blade-example/blade-rabbit-publisher/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + org.springblade + blade-example + ${revision} + + + blade-rabbit-publisher + ${project.artifactId} + jar + + + + org.springblade + blade-core-tool + + + org.springframework.boot + spring-boot-starter-amqp + + + + diff --git a/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/RabbitPublisherApplication.java b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/RabbitPublisherApplication.java new file mode 100644 index 0000000..5aef504 --- /dev/null +++ b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/RabbitPublisherApplication.java @@ -0,0 +1,49 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.publisher; + +import org.springblade.core.launch.BladeApplication; +import org.springframework.amqp.rabbit.annotation.EnableRabbit; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +import static org.springblade.common.constant.LauncherConstant.APPLICATION_RABBIT_PUBLISHER_NAME; + +/** + * Rabbit 生产者启动器 + * + * @author Chill + */ +@EnableRabbit +@EnableScheduling +@SpringBootApplication +public class RabbitPublisherApplication { + + public static void main(String[] args) { + BladeApplication.run(APPLICATION_RABBIT_PUBLISHER_NAME, RabbitPublisherApplication.class, args); + } + +} diff --git a/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/config/RabbitPublisherConfiguration.java b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/config/RabbitPublisherConfiguration.java new file mode 100644 index 0000000..11cc779 --- /dev/null +++ b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/config/RabbitPublisherConfiguration.java @@ -0,0 +1,50 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.publisher.config; + +import org.springframework.amqp.core.TopicExchange; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Rabbit 生产者配置 + * + * @author Chill + */ +@Configuration +public class RabbitPublisherConfiguration { + + + /** + * 交换机名 + */ + public static final String EXCHANGE_NAME = "blade.amqp.exchange"; + + @Bean + public TopicExchange exchange() { + return new TopicExchange(EXCHANGE_NAME); + } +} diff --git a/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/service/RabbitMessageService.java b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/service/RabbitMessageService.java new file mode 100644 index 0000000..66834c7 --- /dev/null +++ b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/service/RabbitMessageService.java @@ -0,0 +1,90 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.publisher.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.rabbit.publisher.config.RabbitPublisherConfiguration; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.stereotype.Service; + +/** + * Rabbit 消息生产者服务 + * + * @author Chill + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class RabbitMessageService { + + private final RabbitTemplate rabbitTemplate; + + /** + * 发送示例消息 + * + * @param routingKey 路由键 + * @param message 消息内容 + */ + public void sendDemoMessage(String routingKey, Object message) { + try { + rabbitTemplate.convertAndSend(RabbitPublisherConfiguration.EXCHANGE_NAME, routingKey, message); + log.info("发送示例消息成功: routingKey={}, message={}", routingKey, message); + } catch (Exception e) { + log.error("发送示例消息失败: routingKey={}, message={}", routingKey, message, e); + } + } + + /** + * 发送普通消息 + * + * @param routingKey 路由键 + * @param message 消息内容 + */ + public void sendMessage(String routingKey, Object message) { + try { + rabbitTemplate.convertAndSend(RabbitPublisherConfiguration.EXCHANGE_NAME, routingKey, message); + log.info("发送消息成功: routingKey={}, message={}", routingKey, message); + } catch (Exception e) { + log.error("发送消息失败: routingKey={}, message={}", routingKey, message, e); + } + } + + /** + * 发送字节数组消息 + * + * @param routingKey 路由键 + * @param payload 消息载荷 + */ + public void sendPayload(String routingKey, byte[] payload) { + try { + rabbitTemplate.convertAndSend(RabbitPublisherConfiguration.EXCHANGE_NAME, routingKey, payload); + log.info("发送载荷消息成功: routingKey={}, payloadSize={}", routingKey, payload.length); + } catch (Exception e) { + log.error("发送载荷消息失败: routingKey={}, payloadSize={}", routingKey, payload.length, e); + } + } +} diff --git a/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/simulator/MessageSimulator.java b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/simulator/MessageSimulator.java new file mode 100644 index 0000000..75c59bc --- /dev/null +++ b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/simulator/MessageSimulator.java @@ -0,0 +1,192 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.publisher.simulator; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.rabbit.publisher.service.RabbitMessageService; +import org.springblade.rabbit.publisher.support.MessageData; +import org.springblade.core.tool.jackson.JsonUtil; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * Rabbit 消息模拟器(模拟设备消息的转发) + * + * @author Chill + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class MessageSimulator implements CommandLineRunner { + + private final RabbitMessageService rabbitMessageService; + private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4); + + @Override + public void run(String... args) { + log.info("启动 Rabbit 消息生产者模拟器"); + + // 启动定时任务 + startScheduledTasks(); + } + + /** + * 启动定时任务 + */ + private void startScheduledTasks() { + // 定期发送示例消息(每30秒) + scheduler.scheduleAtFixedRate(this::sendDemoMessage, 10, 30, TimeUnit.SECONDS); + + // 定期发送数据消息(每60秒) + scheduler.scheduleAtFixedRate(this::sendDataMessage, 15, 60, TimeUnit.SECONDS); + + // 定期发送通知消息(每120秒) + scheduler.scheduleAtFixedRate(this::sendNotifyMessage, 20, 120, TimeUnit.SECONDS); + + // 定期发送事件消息(每180秒) + scheduler.scheduleAtFixedRate(this::sendEventMessage, 25, 180, TimeUnit.SECONDS); + + log.info("已启动消息生产者定时任务"); + } + + /** + * 发送示例消息 + */ + private void sendDemoMessage() { + try { + MessageData messageData = new MessageData(); + messageData.setId(UUID.randomUUID().toString().replace("-", "")); + messageData.setTimestamp(System.currentTimeMillis()); + messageData.setType("demo"); + + Map data = new HashMap<>(); + data.put("temperature", 20 + Math.random() * 10); + data.put("humidity", 40 + Math.random() * 30); + data.put("status", Math.random() > 0.5 ? "online" : "offline"); + messageData.setData(data); + + String routingKey = "demo.test." + System.currentTimeMillis(); + rabbitMessageService.sendDemoMessage(routingKey, JsonUtil.toJson(messageData)); + + log.debug("已发送示例消息: routingKey={}", routingKey); + } catch (Exception e) { + log.error("发送示例消息失败", e); + } + } + + /** + * 发送数据消息 + */ + private void sendDataMessage() { + try { + MessageData messageData = new MessageData(); + messageData.setId(UUID.randomUUID().toString().replace("-", "")); + messageData.setTimestamp(System.currentTimeMillis()); + messageData.setType("data"); + + Map data = new HashMap<>(); + data.put("deviceId", "device_" + (int)(Math.random() * 100)); + data.put("value", Math.random() * 100); + data.put("unit", "celsius"); + messageData.setData(data); + + String routingKey = "demo.data." + System.currentTimeMillis(); + rabbitMessageService.sendDemoMessage(routingKey, JsonUtil.toJson(messageData)); + + log.debug("已发送数据消息: routingKey={}", routingKey); + } catch (Exception e) { + log.error("发送数据消息失败", e); + } + } + + /** + * 发送通知消息 + */ + private void sendNotifyMessage() { + try { + MessageData messageData = new MessageData(); + messageData.setId(UUID.randomUUID().toString().replace("-", "")); + messageData.setTimestamp(System.currentTimeMillis()); + messageData.setType("notify"); + + Map data = new HashMap<>(); + data.put("title", "系统通知"); + data.put("content", "这是一条通知消息"); + data.put("level", Math.random() > 0.5 ? "info" : "warning"); + messageData.setData(data); + + String routingKey = "message.notify." + System.currentTimeMillis(); + rabbitMessageService.sendMessage(routingKey, JsonUtil.toJson(messageData)); + + log.debug("已发送通知消息: routingKey={}", routingKey); + } catch (Exception e) { + log.error("发送通知消息失败", e); + } + } + + /** + * 发送事件消息 + */ + private void sendEventMessage() { + try { + MessageData messageData = new MessageData(); + messageData.setId(UUID.randomUUID().toString().replace("-", "")); + messageData.setTimestamp(System.currentTimeMillis()); + messageData.setType("event"); + + Map data = new HashMap<>(); + data.put("eventType", "user_action"); + data.put("action", Math.random() > 0.5 ? "login" : "logout"); + data.put("userId", "user_" + (int)(Math.random() * 1000)); + messageData.setData(data); + + String routingKey = "message.event." + System.currentTimeMillis(); + rabbitMessageService.sendMessage(routingKey, JsonUtil.toJson(messageData)); + + log.debug("已发送事件消息: routingKey={}", routingKey); + } catch (Exception e) { + log.error("发送事件消息失败", e); + } + } + + /** + * 停止模拟器 + */ + public void stop() { + if (!scheduler.isShutdown()) { + scheduler.shutdown(); + log.info("消息生产者模拟器已停止"); + } + } +} diff --git a/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/support/MessageData.java b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/support/MessageData.java new file mode 100644 index 0000000..48e22cf --- /dev/null +++ b/blade-example/blade-rabbit-publisher/src/main/java/org/springblade/rabbit/publisher/support/MessageData.java @@ -0,0 +1,69 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.rabbit.publisher.support; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * Rabbit 消息数据 + * + * @author Chill + */ +@Data +public class MessageData implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 消息ID + */ + private String id; + + /** + * 消息类型 + */ + private String type; + + /** + * 时间戳 + */ + private Long timestamp; + + /** + * 消息数据 + */ + private Object data; + + /** + * 消息版本 + */ + private String version = "1.0"; + +} diff --git a/blade-example/blade-rabbit-publisher/src/main/resources/application.yml b/blade-example/blade-rabbit-publisher/src/main/resources/application.yml new file mode 100644 index 0000000..9164f24 --- /dev/null +++ b/blade-example/blade-rabbit-publisher/src/main/resources/application.yml @@ -0,0 +1,15 @@ +server: + port: 8301 + +spring: + # RabbitMQ 配置 + rabbitmq: + host: localhost + port: 5672 + username: root + password: root + virtual-host: / + connection-timeout: 15000 + # 生产者配置 + publisher-confirm-type: correlated + publisher-returns: true \ No newline at end of file diff --git a/blade-example/blade-seata-order/README.md b/blade-example/blade-seata-order/README.md new file mode 100644 index 0000000..d8e10fd --- /dev/null +++ b/blade-example/blade-seata-order/README.md @@ -0,0 +1,30 @@ +-- 创建 order库、业务表、undo_log表 +create database seata_order; +use seata_order; + +DROP TABLE IF EXISTS `tb_order`; +CREATE TABLE `tb_order` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` varchar(255) DEFAULT NULL, + `commodity_code` varchar(255) DEFAULT NULL, + `count` int(11) DEFAULT 0, + `money` int(11) DEFAULT 0, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `undo_log` +( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `branch_id` BIGINT(20) NOT NULL, + `xid` VARCHAR(100) NOT NULL, + `context` VARCHAR(128) NOT NULL, + `rollback_info` LONGBLOB NOT NULL, + `log_status` INT(11) NOT NULL, + `log_created` DATETIME NOT NULL, + `log_modified` DATETIME NOT NULL, + `ext` VARCHAR(100) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8; \ No newline at end of file diff --git a/blade-example/blade-seata-order/pom.xml b/blade-example/blade-seata-order/pom.xml new file mode 100644 index 0000000..c388c50 --- /dev/null +++ b/blade-example/blade-seata-order/pom.xml @@ -0,0 +1,27 @@ + + + + blade-example + org.springblade + ${revision} + + 4.0.0 + + blade-seata-order + ${project.artifactId} + jar + + + + org.springblade + blade-core-boot + + + org.springblade + blade-starter-transaction + + + + diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/SeataOrderApplication.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/SeataOrderApplication.java new file mode 100644 index 0000000..cc0ec4d --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/SeataOrderApplication.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * 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 + *

+ * http://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.seata.order; + +import org.springblade.common.constant.LauncherConstant; +import org.springblade.core.cloud.feign.EnableBladeFeign; +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.transaction.annotation.SeataCloudApplication; + +/** + * Order启动器 + * + * @author Chill + */ +@EnableBladeFeign +@SeataCloudApplication +public class SeataOrderApplication { + + public static void main(String[] args) { + BladeApplication.run(LauncherConstant.APPLICATION_SEATA_ORDER_NAME, SeataOrderApplication.class, args); + } + +} + diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/controller/OrderController.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/controller/OrderController.java new file mode 100644 index 0000000..fe6ddf5 --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/controller/OrderController.java @@ -0,0 +1,34 @@ +package org.springblade.seata.order.controller; + +import lombok.AllArgsConstructor; +import org.springblade.core.tool.api.R; +import org.springblade.seata.order.service.IOrderService; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * OrderController + * + * @author Chill + */ +@RestController +@RequestMapping("order") +@AllArgsConstructor +public class OrderController { + + private final IOrderService orderService; + + /** + * 创建订单 + * + * @param userId 用户id + * @param commodityCode 商品代码 + * @param count 数量 + * @return boolean + */ + @RequestMapping("/create") + public R createOrder(String userId, String commodityCode, Integer count) { + return R.status(orderService.createOrder(userId, commodityCode, count)); + } + +} diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/entity/Order.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/entity/Order.java new file mode 100644 index 0000000..48ac397 --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/entity/Order.java @@ -0,0 +1,33 @@ +package org.springblade.seata.order.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * Order + * + * @author Chill + */ +@Data +@Accessors(chain = true) +@TableName("tb_order") +public class Order implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + private String userId; + private String commodityCode; + private Integer count; + private BigDecimal money; + +} diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/IStorageClient.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/IStorageClient.java new file mode 100644 index 0000000..1605f1d --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/IStorageClient.java @@ -0,0 +1,25 @@ +package org.springblade.seata.order.feign; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +/** + * StorageClient + * + * @author Chill + */ +@FeignClient(name = "blade-seata-storage", fallback = StorageClientFallback.class) +public interface IStorageClient { + + /** + * 减库存 + * + * @param commodityCode 商品代码 + * @param count 数量 + * @return boolean + */ + @GetMapping("/storage/deduct") + int deduct(@RequestParam("commodityCode") String commodityCode, @RequestParam("count") Integer count); + +} diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/StorageClientFallback.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/StorageClientFallback.java new file mode 100644 index 0000000..6bfaeef --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/feign/StorageClientFallback.java @@ -0,0 +1,18 @@ +package org.springblade.seata.order.feign; + +import org.springframework.stereotype.Component; + +/** + * StorageClientFallback + * + * @author Chill + */ +@Component +public class StorageClientFallback implements IStorageClient { + + @Override + public int deduct(String commodityCode, Integer count) { + return -1; + } + +} diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.java new file mode 100644 index 0000000..078c980 --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.java @@ -0,0 +1,12 @@ +package org.springblade.seata.order.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springblade.seata.order.entity.Order; + +/** + * OrderMapper + * + * @author Chill + */ +public interface OrderMapper extends BaseMapper { +} diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.xml b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.xml new file mode 100644 index 0000000..da6e86c --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/mapper/OrderMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/IOrderService.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/IOrderService.java new file mode 100644 index 0000000..81a3a4d --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/IOrderService.java @@ -0,0 +1,23 @@ +package org.springblade.seata.order.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.springblade.seata.order.entity.Order; + +/** + * IOrderService + * + * @author Chill + */ +public interface IOrderService extends IService { + + /** + * 创建订单 + * + * @param userId 用户id + * @param commodityCode 商品代码 + * @param count 数量 + * @return boolean + */ + boolean createOrder(String userId, String commodityCode, Integer count); + +} diff --git a/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/impl/OrderServiceImpl.java b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/impl/OrderServiceImpl.java new file mode 100644 index 0000000..fb6e66d --- /dev/null +++ b/blade-example/blade-seata-order/src/main/java/org/springblade/seata/order/service/impl/OrderServiceImpl.java @@ -0,0 +1,48 @@ +package org.springblade.seata.order.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import io.seata.spring.annotation.GlobalTransactional; +import lombok.AllArgsConstructor; +import org.springblade.core.log.exception.ServiceException; +import org.springblade.seata.order.entity.Order; +import org.springblade.seata.order.feign.IStorageClient; +import org.springblade.seata.order.mapper.OrderMapper; +import org.springblade.seata.order.service.IOrderService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; + +/** + * OrderServiceImpl + * + * @author Chill + */ +@Service +@AllArgsConstructor +public class OrderServiceImpl extends ServiceImpl implements IOrderService { + + private final IStorageClient storageClient; + + @Override + @GlobalTransactional + @Transactional(rollbackFor = Exception.class) + public boolean createOrder(String userId, String commodityCode, Integer count) { + int maxCount = 100; + BigDecimal orderMoney = new BigDecimal(count).multiply(new BigDecimal(5)); + Order order = new Order() + .setUserId(userId) + .setCommodityCode(commodityCode) + .setCount(count) + .setMoney(orderMoney); + int cnt1 = baseMapper.insert(order); + int cnt2 = storageClient.deduct(commodityCode, count); + if (cnt2 < 0) { + throw new ServiceException("创建订单失败"); + } else if (count > maxCount) { + throw new ServiceException("超过订单最大值,创建订单失败"); + } + return cnt1 > 0 && cnt2 > 0; + } + +} diff --git a/blade-example/blade-seata-order/src/main/resources/application-dev.yml b/blade-example/blade-seata-order/src/main/resources/application-dev.yml new file mode 100644 index 0000000..378c331 --- /dev/null +++ b/blade-example/blade-seata-order/src/main/resources/application-dev.yml @@ -0,0 +1,15 @@ +#服务器端口 +server: + port: 8501 + +#数据源配置 +spring: + datasource: + url: jdbc:mysql://localhost:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + username: root + password: root + +#常规配置 +blade: + mybatis-plus: + tenant-mode: false diff --git a/blade-example/blade-seata-storage/README.md b/blade-example/blade-seata-storage/README.md new file mode 100644 index 0000000..62575a7 --- /dev/null +++ b/blade-example/blade-seata-storage/README.md @@ -0,0 +1,33 @@ +-- 创建 storage库、业务表、undo_log表 +create database seata_storage; +use seata_storage; + +DROP TABLE IF EXISTS `tb_storage`; +CREATE TABLE `tb_storage` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `commodity_code` varchar(255) DEFAULT NULL, + `count` int(11) DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY (`commodity_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `undo_log` +( + `id` BIGINT(20) NOT NULL AUTO_INCREMENT, + `branch_id` BIGINT(20) NOT NULL, + `xid` VARCHAR(100) NOT NULL, + `context` VARCHAR(128) NOT NULL, + `rollback_info` LONGBLOB NOT NULL, + `log_status` INT(11) NOT NULL, + `log_created` DATETIME NOT NULL, + `log_modified` DATETIME NOT NULL, + `ext` VARCHAR(100) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8; + +-- 初始化库存模拟数据 +INSERT INTO seata_storage.tb_storage (id, commodity_code, count) VALUES (1, 'product-1', 9999999); +INSERT INTO seata_storage.tb_storage (id, commodity_code, count) VALUES (2, 'product-2', 0); \ No newline at end of file diff --git a/blade-example/blade-seata-storage/pom.xml b/blade-example/blade-seata-storage/pom.xml new file mode 100644 index 0000000..aba6cea --- /dev/null +++ b/blade-example/blade-seata-storage/pom.xml @@ -0,0 +1,28 @@ + + + + blade-example + org.springblade + ${revision} + + 4.0.0 + + blade-seata-storage + ${project.artifactId} + jar + + + + org.springblade + blade-core-boot + + + org.springblade + blade-starter-transaction + + + + + diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/SeataStorageApplication.java b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/SeataStorageApplication.java new file mode 100644 index 0000000..3108686 --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/SeataStorageApplication.java @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com). + *

+ * 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 + *

+ * http://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.seata.storage; + +import org.springblade.common.constant.LauncherConstant; +import org.springblade.core.cloud.feign.EnableBladeFeign; +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.transaction.annotation.SeataCloudApplication; + +/** + * Storage启动器 + * + * @author Chill + */ +@EnableBladeFeign +@SeataCloudApplication +public class SeataStorageApplication { + + public static void main(String[] args) { + BladeApplication.run(LauncherConstant.APPLICATION_SEATA_STORAGE_NAME, SeataStorageApplication.class, args); + } + +} + diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/controller/StorageController.java b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/controller/StorageController.java new file mode 100644 index 0000000..b2b3bab --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/controller/StorageController.java @@ -0,0 +1,31 @@ +package org.springblade.seata.storage.controller; + +import lombok.AllArgsConstructor; +import org.springblade.seata.storage.service.IStorageService; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * StorageController + * + * @author Chill + */ +@RestController +@AllArgsConstructor +@RequestMapping("storage") +public class StorageController { + + private final IStorageService storageService; + + /** + * 减库存 + * + * @param commodityCode 商品代码 + * @param count 数量 + */ + @RequestMapping(path = "/deduct") + public int deduct(String commodityCode, Integer count) { + return storageService.deduct(commodityCode, count); + } + +} diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/entity/Storage.java b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/entity/Storage.java new file mode 100644 index 0000000..b1e80ac --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/entity/Storage.java @@ -0,0 +1,26 @@ +package org.springblade.seata.storage.entity; + + +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * storage + * + * @author Chill + */ +@Data +@TableName("tb_storage") +public class Storage implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + private String commodityCode; + private Long count; + +} diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.java b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.java new file mode 100644 index 0000000..800ac01 --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.java @@ -0,0 +1,12 @@ +package org.springblade.seata.storage.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springblade.seata.storage.entity.Storage; + +/** + * StorageMapper + * + * @author Chill + */ +public interface StorageMapper extends BaseMapper { +} diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.xml b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.xml new file mode 100644 index 0000000..c2c8a9a --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/mapper/StorageMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/IStorageService.java b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/IStorageService.java new file mode 100644 index 0000000..1b2fe40 --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/IStorageService.java @@ -0,0 +1,22 @@ +package org.springblade.seata.storage.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.springblade.seata.storage.entity.Storage; + +/** + * IStorageService + * + * @author Chill + */ +public interface IStorageService extends IService { + + /** + * 减库存 + * + * @param commodityCode 商品代码 + * @param count 数量 + * @return boolean + */ + int deduct(String commodityCode, int count); + +} diff --git a/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/impl/StorageServiceImpl.java b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/impl/StorageServiceImpl.java new file mode 100644 index 0000000..fc1eacc --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/java/org/springblade/seata/storage/service/impl/StorageServiceImpl.java @@ -0,0 +1,30 @@ +package org.springblade.seata.storage.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springblade.seata.storage.entity.Storage; +import org.springblade.seata.storage.mapper.StorageMapper; +import org.springblade.seata.storage.service.IStorageService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * StorageServiceImpl + * + * @author Chill + */ +@Service +public class StorageServiceImpl extends ServiceImpl implements IStorageService { + + @Override + @Transactional(rollbackFor = Exception.class) + public int deduct(String commodityCode, int count) { + Storage storage = baseMapper.selectOne(Wrappers.query().lambda().eq(Storage::getCommodityCode, commodityCode)); + if (storage.getCount() < count) { + throw new RuntimeException("超过库存数,扣除失败!"); + } + storage.setCount(storage.getCount() - count); + return baseMapper.updateById(storage); + } + +} diff --git a/blade-example/blade-seata-storage/src/main/resources/application-dev.yml b/blade-example/blade-seata-storage/src/main/resources/application-dev.yml new file mode 100644 index 0000000..6b80b65 --- /dev/null +++ b/blade-example/blade-seata-storage/src/main/resources/application-dev.yml @@ -0,0 +1,15 @@ +#服务器端口 +server: + port: 8502 + +#数据源配置 +spring: + datasource: + url: jdbc:mysql://localhost:3306/seata_storage?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + username: root + password: root + +#常规配置 +blade: + mybatis-plus: + tenant-mode: false diff --git a/blade-example/blade-sharding-jdbc/pom.xml b/blade-example/blade-sharding-jdbc/pom.xml new file mode 100644 index 0000000..565ad90 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/pom.xml @@ -0,0 +1,41 @@ + + + + blade-example + org.springblade + ${revision} + + 4.0.0 + + blade-sharding-jdbc + ${project.artifactId} + jar + + + + + org.springblade + blade-core-boot + + + org.springblade + blade-starter-swagger + + + org.springblade + blade-starter-tenant + + + org.springblade + blade-starter-sharding + + + org.springblade + blade-core-auto + provided + + + + diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/ShardingApplication.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/ShardingApplication.java new file mode 100644 index 0000000..3dd0729 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/ShardingApplication.java @@ -0,0 +1,46 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.sharding; + +import org.springblade.core.cloud.client.BladeCloudApplication; +import org.springblade.core.launch.BladeApplication; + +import static org.springblade.common.constant.LauncherConstant.APPLICATION_SHARDING_NAME; + +/** + * 启动器 + * + * @author Chill + */ +@BladeCloudApplication +public class ShardingApplication { + + public static void main(String[] args) { + BladeApplication.run(APPLICATION_SHARDING_NAME, ShardingApplication.class, args); + } + +} + diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/controller/NoticeController.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/controller/NoticeController.java new file mode 100644 index 0000000..c47927d --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/controller/NoticeController.java @@ -0,0 +1,150 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.sharding.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.mp.support.Condition; +import org.springblade.core.mp.support.Query; +import org.springblade.core.sharding.annotation.ShardingDS; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.Func; +import org.springblade.sharding.entity.Notice; +import org.springblade.sharding.service.INoticeService; +import org.springblade.sharding.vo.NoticeVO; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 控制器 + * + * @author Chill + */ +@RestController +@RequestMapping("notice") +@AllArgsConstructor +@Tag(name = "通知公告", description = "通知公告接口") +public class NoticeController extends BladeController { + + private final INoticeService noticeService; + + + /** + * 新增或修改(使用sharding) + */ + @ShardingDS + @PostMapping("/submit") + @ApiOperationSupport(order = 1) + @Operation(summary = "新增或修改", description = "传入notice") + public R submit(@RequestBody Notice notice) { + return R.status(noticeService.saveOrUpdate(notice)); + } + + /** + * 分页(使用sharding) + */ + @ShardingDS + @GetMapping("/list") + @Parameters({ + @Parameter(name = "category", description = "公告类型", in = ParameterIn.QUERY, schema = @Schema(type = "integer")), + @Parameter(name = "title", description = "公告标题", in = ParameterIn.QUERY, schema = @Schema(type = "string")) + }) + @ApiOperationSupport(order = 2) + @Operation(summary = "分页", description = "传入notice") + public R> list(@Parameter(hidden = true) @RequestParam Map notice, Query query) { + IPage pages = noticeService.page(Condition.getPage(query), Condition.getQueryWrapper(notice, Notice.class)); + return R.data(pages); + } + + /** + * 多表联合查询自定义分页 + */ + @GetMapping("/page") + @Parameters({ + @Parameter(name = "category", description = "公告类型", in = ParameterIn.QUERY, schema = @Schema(type = "integer")), + @Parameter(name = "title", description = "公告标题", in = ParameterIn.QUERY, schema = @Schema(type = "string")) + }) + @ApiOperationSupport(order = 3) + @Operation(summary = "分页", description = "传入notice") + public R> page(@Parameter(hidden = true) NoticeVO notice, Query query) { + IPage pages = noticeService.selectNoticePage(Condition.getPage(query), notice); + return R.data(pages); + } + + /** + * 新增 + */ + @PostMapping("/save") + @ApiOperationSupport(order = 4) + @Operation(summary = "新增", description = "传入notice") + public R save(@RequestBody Notice notice) { + return R.status(noticeService.save(notice)); + } + + /** + * 修改 + */ + @PostMapping("/update") + @ApiOperationSupport(order = 5) + @Operation(summary = "修改", description = "传入notice") + public R update(@RequestBody Notice notice) { + return R.status(noticeService.updateById(notice)); + } + + /** + * 详情 + */ + @GetMapping("/detail") + @ApiOperationSupport(order = 6) + @Operation(summary = "详情", description = "传入notice") + public R detail(Notice notice) { + Notice detail = noticeService.getOne(Condition.getQueryWrapper(notice)); + return R.data(detail); + } + + /** + * 删除 + */ + @ShardingDS + @PostMapping("/remove") + @ApiOperationSupport(order = 7) + @Operation(summary = "逻辑删除", description = "传入notice") + public R remove(@Parameter(name = "主键集合") @RequestParam String ids) { + boolean temp = noticeService.deleteLogic(Func.toLongList(ids)); + return R.status(temp); + } + + +} diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/entity/Notice.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/entity/Notice.java new file mode 100644 index 0000000..9d25f37 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/entity/Notice.java @@ -0,0 +1,81 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.sharding.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.mp.base.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 实体类 + * + * @author Chill + */ +@Data +@TableName("blade_notice") +@EqualsAndHashCode(callSuper = true) +public class Notice extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户ID + */ + @Schema(description = "租户ID") + private String tenantId; + + /** + * 标题 + */ + @Schema(description = "标题") + private String title; + + /** + * 通知类型 + */ + @Schema(description = "通知类型") + private Integer category; + + /** + * 发布日期 + */ + @Schema(description = "发布日期") + private Date releaseTime; + + /** + * 内容 + */ + @Schema(description = "内容") + private String content; + + +} diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.java new file mode 100644 index 0000000..e34712c --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.java @@ -0,0 +1,51 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.sharding.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.sharding.entity.Notice; +import org.springblade.sharding.vo.NoticeVO; + +import java.util.List; + +/** + * Mapper 接口 + * + * @author Chill + */ +public interface NoticeMapper extends BaseMapper { + + /** + * 自定义分页 + * + * @param page 分页 + * @param notice 实体 + * @return List + */ + List selectNoticePage(IPage page, NoticeVO notice); + +} diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.xml b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.xml new file mode 100644 index 0000000..d906919 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/mapper/NoticeMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/INoticeService.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/INoticeService.java new file mode 100644 index 0000000..2b78465 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/INoticeService.java @@ -0,0 +1,49 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.sharding.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.core.mp.base.BaseService; +import org.springblade.sharding.entity.Notice; +import org.springblade.sharding.vo.NoticeVO; + + +/** + * 服务类 + * + * @author Chill + */ +public interface INoticeService extends BaseService { + + /** + * 自定义分页 + * @param page + * @param notice + * @return + */ + IPage selectNoticePage(IPage page, NoticeVO notice); + +} diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/impl/NoticeServiceImpl.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/impl/NoticeServiceImpl.java new file mode 100644 index 0000000..37db230 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/service/impl/NoticeServiceImpl.java @@ -0,0 +1,52 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.sharding.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.core.mp.base.BaseServiceImpl; +import org.springblade.core.secure.utils.AuthUtil; +import org.springblade.sharding.entity.Notice; +import org.springblade.sharding.mapper.NoticeMapper; +import org.springblade.sharding.service.INoticeService; +import org.springblade.sharding.vo.NoticeVO; +import org.springframework.stereotype.Service; + +/** + * 服务实现类 + * + * @author Chill + */ +@Service +public class NoticeServiceImpl extends BaseServiceImpl implements INoticeService { + + @Override + public IPage selectNoticePage(IPage page, NoticeVO notice) { + // 若不使用mybatis-plus自带的分页方法,则不会自动带入tenantId,所以我们需要自行注入 + notice.setTenantId(AuthUtil.getTenantId()); + return page.setRecords(baseMapper.selectNoticePage(page, notice)); + } + +} diff --git a/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/vo/NoticeVO.java b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/vo/NoticeVO.java new file mode 100644 index 0000000..ff398d5 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/java/org/springblade/sharding/vo/NoticeVO.java @@ -0,0 +1,23 @@ +package org.springblade.sharding.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.sharding.entity.Notice; + +/** + * 通知公告视图类 + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class NoticeVO extends Notice { + + @Schema(description = "通知类型名") + private String categoryName; + + @Schema(description = "租户编号") + private String tenantId; + +} diff --git a/blade-example/blade-sharding-jdbc/src/main/resources/application-dev.yml b/blade-example/blade-sharding-jdbc/src/main/resources/application-dev.yml new file mode 100644 index 0000000..75a5ab5 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/resources/application-dev.yml @@ -0,0 +1,61 @@ +#不分库仅分表 +spring: + #主数据源 + datasource: + # MySql + url: jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + username: root + password: root + #sharding数据源 + shardingsphere: + datasource: + #定义的数据库集合 + names: ds1 + #数据库连接别名 + ds1: + #数据连接池类型 + type: com.alibaba.druid.pool.DruidDataSource + #驱动类型 + driver-class-name: com.mysql.cj.jdbc.Driver + #jdbc地址 + url: jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + #数据库账号 + username: root + #数据库密码 + password: root + rules: + sharding: + #主键生成类型 + key-generators: + snowflake: + type: SNOWFLAKE + tables: + #分表代号 + blade_notice: + #实际节点名称,格式为 库名$->{0..n1}.表名$->{0..n2} + #blade_notice_$->{1..2} 代表有 blade_notice_1和blade_notice_2两个表 + actual-data-nodes: ds1.blade_notice_$->{1..2} + #分表策略 + table-strategy: + standard: + #分表列名 + sharding-column: id + #分表算法名,不可用下划线 + sharding-algorithm-name: blade-notice-inline + #主键生成策略 + key-generate-strategy: + #主键名 + column: id + #策略算法 + key-generator-name: snowflake + #分库分表算法 + sharding-algorithms: + #分表算法名 + blade-notice-inline: + #算法类型 + type: INLINE + props: + #算法表达式 + #表达式写法非常自由,以下配置仅作为参考 + #blade_notice_$->{id % 2 + 1} 代表对id进行取余,根据id的基偶来指向blade_notice_1和blade_notice_2 + algorithm-expression: blade_notice_$->{id % 2 + 1} diff --git a/blade-example/blade-sharding-jdbc/src/main/resources/application-prod.yml b/blade-example/blade-sharding-jdbc/src/main/resources/application-prod.yml new file mode 100644 index 0000000..e7113e8 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/resources/application-prod.yml @@ -0,0 +1,71 @@ +#不分表仅分库 +spring: + #主数据源 + datasource: + # MySql + url: jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + username: root + password: root + #sharding数据源 + shardingsphere: + datasource: + names: ds1,ds2 + #数据库连接别名 + ds1: + #数据连接池类型 + type: com.alibaba.druid.pool.DruidDataSource + #驱动类型 + driver-class-name: com.mysql.cj.jdbc.Driver + #jdbc地址 + url: jdbc:mysql://localhost:3306/bladex1?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + #数据库账号 + username: root + #数据库密码 + password: root + #数据库连接别名 + ds2: + #数据连接池类型 + type: com.alibaba.druid.pool.DruidDataSource + #驱动类型 + driver-class-name: com.mysql.cj.jdbc.Driver + #jdbc地址 + jdbc-url: jdbc:mysql://localhost:3306/bladex2?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + #数据库账号 + username: root + #数据库密码 + password: root + rules: + sharding: + #主键生成类型 + key-generators: + snowflake: + type: SNOWFLAKE + #默认分库策略 + default-database-strategy: + standard: + #列名 + sharding-column: id + #算法名 + sharding-algorithm-name: database-inline + tables: + #表名代号 + blade_notice: + #实际节点名称,格式为 库名$->{0..n1}.表名 + actual-data-nodes: ds$->{1..2}.blade_notice + #主键生成策略 + key-generate-strategy: + #主键名 + column: id + #策略算法 + key-generator-name: snowflake + #分库分表算法 + sharding-algorithms: + #分库算法名 + database-inline: + #算法类型 + type: INLINE + props: + #算法表达式 + #表达式写法非常自由,以下配置仅作为参考 + #ds$->{id % 2 + 1} 代表对id进行取余,根据id的基偶来指向 ds1和ds2 + algorithm-expression: ds$->{id % 2 + 1} diff --git a/blade-example/blade-sharding-jdbc/src/main/resources/application-test.yml b/blade-example/blade-sharding-jdbc/src/main/resources/application-test.yml new file mode 100644 index 0000000..96d874d --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/resources/application-test.yml @@ -0,0 +1,88 @@ +#分库+分表 +spring: + #主数据源 + datasource: + # MySql + url: jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + username: root + password: root + #sharding数据源 + shardingsphere: + datasource: + names: ds1,ds2 + #数据库连接别名 + ds1: + #数据连接池类型 + type: com.alibaba.druid.pool.DruidDataSource + #驱动类型 + driver-class-name: com.mysql.cj.jdbc.Driver + #jdbc地址 + url: jdbc:mysql://localhost:3306/bladex1?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + #数据库账号 + username: root + #数据库密码 + password: root + #数据库连接别名 + ds2: + #数据连接池类型 + type: com.alibaba.druid.pool.DruidDataSource + #驱动类型 + driver-class-name: com.mysql.cj.jdbc.Driver + #jdbc地址 + jdbc-url: jdbc:mysql://localhost:3306/bladex2?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true + #数据库账号 + username: root + #数据库密码 + password: root + rules: + sharding: + #主键生成类型 + key-generators: + snowflake: + type: SNOWFLAKE + #默认分库策略 + default-database-strategy: + standard: + #列名 + sharding-column: id + #算法名 + sharding-algorithm-name: database-inline + tables: + #分表代号 + blade_notice: + #实际节点名称,格式为 库名$->{0..n1}.表名$->{0..n2} + #ds$->{1..2}.blade_notice_$->{1..2} 代表有 ds1和ds2两个库以及blade_notice_1和blade_notice_2两个表 + actual-data-nodes: ds$->{1..2}.blade_notice_$->{1..2} + #分表策略 + table-strategy: + standard: + #分表列名 + sharding-column: id + #分表算法名,不可用下划线 + sharding-algorithm-name: blade-notice-inline + #主键生成策略 + key-generate-strategy: + #主键名 + column: id + #策略算法 + key-generator-name: snowflake + #分库分表算法 + sharding-algorithms: + #分库算法名 + database-inline: + #算法类型 + type: INLINE + props: + #算法表达式 + #表达式写法非常自由,以下配置仅作为参考 + #ds$->{id % 2 + 1} 代表对id进行取余,根据id的基偶来指向 ds1和ds2 + algorithm-expression: ds$->{id % 2 + 1} + #分表算法名 + blade-notice-inline: + #算法类型 + type: INLINE + props: + #算法表达式 + #表达式写法非常自由,以下配置仅作为参考 + #blade_notice_$->{id % 2 + 1} 代表对id进行取余,根据id的基偶来指向blade_notice_1和blade_notice_2 + algorithm-expression: blade_notice_$->{id % 2 + 1} diff --git a/blade-example/blade-sharding-jdbc/src/main/resources/application.yml b/blade-example/blade-sharding-jdbc/src/main/resources/application.yml new file mode 100644 index 0000000..4e15840 --- /dev/null +++ b/blade-example/blade-sharding-jdbc/src/main/resources/application.yml @@ -0,0 +1,18 @@ +## server +server: + port: 8701 + +spring: + #sharding通用配置 + shardingsphere: + mode: + #内存模式 + type: Standalone + props: + #是否展示sql + sql-show: true +blade: + #开启分库分表自动配置 + #nacos会覆盖此配置,需要在nacos也配置为true + sharding: + enabled: true diff --git a/blade-example/blade-xxl-job/Dockerfile b/blade-example/blade-xxl-job/Dockerfile new file mode 100644 index 0000000..36b9232 --- /dev/null +++ b/blade-example/blade-xxl-job/Dockerfile @@ -0,0 +1,13 @@ +FROM bladex/alpine-java:openjdk17_cn_slim + +LABEL maintainer="bladejava@qq.com" + +RUN mkdir -p /blade/xxljob + +WORKDIR /blade/xxljob + +EXPOSE 7008 + +COPY ./target/blade-xxljob.jar ./app.jar + +ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] diff --git a/blade-example/blade-xxl-job/doc/README.md b/blade-example/blade-xxl-job/doc/README.md new file mode 100644 index 0000000..c358c6b --- /dev/null +++ b/blade-example/blade-xxl-job/doc/README.md @@ -0,0 +1,22 @@ +## 分布式任务调度服务启动 +### XXL-JOB +1. 执行/doc/sql文件夹下的sql脚本 + +2. 拉取docker镜像 + + ```shell + docker pull xuxueli/xxl-job-admin:2.4.0 + ``` + +3. 执行docker命令运行服务 + ```shell + docker run -d --add-host="host.docker.internal:host-gateway" \ + -p 8080:8080 \ + --restart=always \ + --name xxl-job \ + -e TZ="Asia/Shanghai" \ + -e JAVA_OPTS="-Xms512M -Xmx512m" \ + -e PARAMS="--spring.datasource.url=jdbc:mysql://host.docker.internal:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai --spring.datasource.username=root --spring.datasource.password=root" \ + -v ~/docker/xxl-job:/data/applogs \ + xuxueli/xxl-job-admin:2.4.0 + ``` \ No newline at end of file diff --git a/blade-example/blade-xxl-job/doc/sql/xxljob.all.create.sql b/blade-example/blade-xxl-job/doc/sql/xxljob.all.create.sql new file mode 100644 index 0000000..c2f12fa --- /dev/null +++ b/blade-example/blade-xxl-job/doc/sql/xxljob.all.create.sql @@ -0,0 +1,123 @@ +-- -------------------------------------- +-- XXL-JOB v2.4.0 +-- Copyright (c) 2015-present, xuxueli. +-- -------------------------------------- + +CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_unicode_ci; +use `xxl_job`; + +SET NAMES utf8mb4; + +CREATE TABLE `xxl_job_info` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`job_group` int(11) NOT NULL COMMENT '执行器主键ID', +`job_desc` varchar(255) NOT NULL, +`add_time` datetime DEFAULT NULL, +`update_time` datetime DEFAULT NULL, +`author` varchar(64) DEFAULT NULL COMMENT '作者', +`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件', +`schedule_type` varchar(50) NOT NULL DEFAULT 'NONE' COMMENT '调度类型', +`schedule_conf` varchar(128) DEFAULT NULL COMMENT '调度配置,值含义取决于调度类型', +`misfire_strategy` varchar(50) NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略', +`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略', +`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', +`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', +`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略', +`executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位秒', +`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', +`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型', +`glue_source` mediumtext COMMENT 'GLUE源代码', +`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注', +`glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间', +`child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,多个逗号分隔', +`trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态:0-停止,1-运行', +`trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间', +`trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间', +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_log` ( +`id` bigint(20) NOT NULL AUTO_INCREMENT, +`job_group` int(11) NOT NULL COMMENT '执行器主键ID', +`job_id` int(11) NOT NULL COMMENT '任务,主键ID', +`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,本次执行的地址', +`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler', +`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数', +`executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数,格式如 1/2', +`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数', +`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间', +`trigger_code` int(11) NOT NULL COMMENT '调度-结果', +`trigger_msg` text COMMENT '调度-日志', +`handle_time` datetime DEFAULT NULL COMMENT '执行-时间', +`handle_code` int(11) NOT NULL COMMENT '执行-状态', +`handle_msg` text COMMENT '执行-日志', +`alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态:0-默认、1-无需告警、2-告警成功、3-告警失败', +PRIMARY KEY (`id`), +KEY `I_trigger_time` (`trigger_time`), +KEY `I_handle_code` (`handle_code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_log_report` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`trigger_day` datetime DEFAULT NULL COMMENT '调度-时间', +`running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量', +`suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量', +`fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量', +`update_time` datetime DEFAULT NULL, +PRIMARY KEY (`id`), +UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_logglue` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`job_id` int(11) NOT NULL COMMENT '任务,主键ID', +`glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型', +`glue_source` mediumtext COMMENT 'GLUE源代码', +`glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注', +`add_time` datetime DEFAULT NULL, +`update_time` datetime DEFAULT NULL, +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_registry` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`registry_group` varchar(50) NOT NULL, +`registry_key` varchar(255) NOT NULL, +`registry_value` varchar(255) NOT NULL, +`update_time` datetime DEFAULT NULL, +PRIMARY KEY (`id`), +KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_group` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`app_name` varchar(64) NOT NULL COMMENT '执行器AppName', +`title` varchar(12) NOT NULL COMMENT '执行器名称', +`address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型:0=自动注册、1=手动录入', +`address_list` text COMMENT '执行器地址列表,多地址逗号分隔', +`update_time` datetime DEFAULT NULL, +PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_user` ( +`id` int(11) NOT NULL AUTO_INCREMENT, +`username` varchar(50) NOT NULL COMMENT '账号', +`password` varchar(50) NOT NULL COMMENT '密码', +`role` tinyint(4) NOT NULL COMMENT '角色:0-普通用户、1-管理员', +`permission` varchar(255) DEFAULT NULL COMMENT '权限:执行器ID列表,多个逗号分割', +PRIMARY KEY (`id`), +UNIQUE KEY `i_username` (`username`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE `xxl_job_lock` ( +`lock_name` varchar(50) NOT NULL COMMENT '锁名称', +PRIMARY KEY (`lock_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`, `update_time`) VALUES (1, 'blade-xxl-job-executor', '示例执行器', 1, 'http://host.docker.internal:9999', '2024-04-01 00:00:00' ); +INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `schedule_type`, `schedule_conf`, `misfire_strategy`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '测试任务', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', ''); +INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL); +INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock'); + +commit; + diff --git a/blade-example/blade-xxl-job/pom.xml b/blade-example/blade-xxl-job/pom.xml new file mode 100644 index 0000000..5241520 --- /dev/null +++ b/blade-example/blade-xxl-job/pom.xml @@ -0,0 +1,59 @@ + + + + blade-example + org.springblade + ${revision} + + 4.0.0 + + blade-xxl-job + ${project.artifactId} + jar + + + 2.4.0 + + + + + + org.springblade + blade-biz-common + + + org.springblade + blade-core-cloud + + + org.springblade + blade-core-launch + + + + com.xuxueli + xxl-job-core + ${xxljob.version} + + + + + + + io.fabric8 + docker-maven-plugin + + ${docker.fabric.skip} + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + + diff --git a/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/XxlJobApplication.java b/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/XxlJobApplication.java new file mode 100644 index 0000000..5339fac --- /dev/null +++ b/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/XxlJobApplication.java @@ -0,0 +1,44 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.xxljob; + +import org.springblade.common.constant.LauncherConstant; +import org.springblade.core.cloud.client.BladeCloudApplication; +import org.springblade.core.launch.BladeApplication; + +/** + * 启动器 + * + * @author Chill + */ +@BladeCloudApplication +public class XxlJobApplication { + public static void main(String[] args) { + BladeApplication.run(LauncherConstant.APPLICATION_XXLJOB_NAME, XxlJobApplication.class, args); + } + +} + diff --git a/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/config/XxlJobConfig.java b/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/config/XxlJobConfig.java new file mode 100644 index 0000000..9a7023a --- /dev/null +++ b/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/config/XxlJobConfig.java @@ -0,0 +1,77 @@ +package org.springblade.xxljob.config; + +import com.xxl.job.core.executor.impl.XxlJobSpringExecutor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * xxl-job config + * + * @author xuxueli + */ +@Slf4j +@Configuration(proxyBeanMethods = false) +public class XxlJobConfig { + + @Value("${xxl.job.admin.addresses}") + private String adminAddresses; + + @Value("${xxl.job.accessToken}") + private String accessToken; + + @Value("${xxl.job.executor.appname}") + private String appname; + + @Value("${xxl.job.executor.address}") + private String address; + + @Value("${xxl.job.executor.ip}") + private String ip; + + @Value("${xxl.job.executor.port}") + private int port; + + @Value("${xxl.job.executor.logpath}") + private String logPath; + + @Value("${xxl.job.executor.logretentiondays}") + private int logRetentionDays; + + + @Bean + public XxlJobSpringExecutor xxlJobExecutor() { + log.info(">>>>>>>>>>> xxl-job config init."); + XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); + xxlJobSpringExecutor.setAdminAddresses(adminAddresses); + xxlJobSpringExecutor.setAppname(appname); + xxlJobSpringExecutor.setAddress(address); + xxlJobSpringExecutor.setIp(ip); + xxlJobSpringExecutor.setPort(port); + xxlJobSpringExecutor.setAccessToken(accessToken); + xxlJobSpringExecutor.setLogPath(logPath); + xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); + + return xxlJobSpringExecutor; + } + + /** + * 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP; + * + * 1、引入依赖: + * + * org.springframework.cloud + * spring-cloud-commons + * ${version} + * + * + * 2、配置文件,或者容器启动变量 + * spring.cloud.inetutils.preferred-networks: 'xxx.xxx.xxx.' + * + * 3、获取IP + * String ip_ = inetUtils.findFirstNonLoopbackHostInfo().getIpAddress(); + */ + + +} diff --git a/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/handler/SampleXxlJob.java b/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/handler/SampleXxlJob.java new file mode 100644 index 0000000..01a5ba5 --- /dev/null +++ b/blade-example/blade-xxl-job/src/main/java/org/springblade/xxljob/handler/SampleXxlJob.java @@ -0,0 +1,256 @@ +package org.springblade.xxljob.handler; + +import com.xxl.job.core.context.XxlJobHelper; +import com.xxl.job.core.handler.annotation.XxlJob; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +/** + * XxlJob开发示例(Bean模式) + *

+ * 开发步骤: + * 1、任务开发:在Spring Bean实例中,开发Job方法; + * 2、注解配置:为Job方法添加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化方法", destroy = "JobHandler销毁方法")",注解value值对应的是调度中心新建任务的JobHandler属性的值。 + * 3、执行日志:需要通过 "XxlJobHelper.log" 打印执行日志; + * 4、任务结果:默认任务结果为 "成功" 状态,不需要主动设置;如有诉求,比如设置任务结果为失败,可以通过 "XxlJobHelper.handleFail/handleSuccess" 自主设置任务结果; + * + * @author xuxueli 2019-12-11 21:52:51 + */ +@Component +public class SampleXxlJob { + private static Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); + + + /** + * 1、简单任务示例(Bean模式) + */ + @XxlJob("demoJobHandler") + public void demoJobHandler() throws Exception { + String param = XxlJobHelper.getJobParam(); + XxlJobHelper.log("XXL-JOB, Hello World." + param); + + for (int i = 0; i < 5; i++) { + XxlJobHelper.log("beat at:" + i); + TimeUnit.SECONDS.sleep(2); + } + // default success + } + + + /** + * 2、分片广播任务 + */ + @XxlJob("shardingJobHandler") + public void shardingJobHandler() throws Exception { + + // 分片参数 + int shardIndex = XxlJobHelper.getShardIndex(); + int shardTotal = XxlJobHelper.getShardTotal(); + + XxlJobHelper.log("分片参数:当前分片序号 = {}, 总分片数 = {}", shardIndex, shardTotal); + + // 业务逻辑 + for (int i = 0; i < shardTotal; i++) { + if (i == shardIndex) { + XxlJobHelper.log("第 {} 片, 命中分片开始处理", i); + } else { + XxlJobHelper.log("第 {} 片, 忽略", i); + } + } + + } + + + /** + * 3、命令行任务 + */ + @XxlJob("commandJobHandler") + public void commandJobHandler() throws Exception { + String command = XxlJobHelper.getJobParam(); + int exitValue = -1; + + BufferedReader bufferedReader = null; + try { + // command process + ProcessBuilder processBuilder = new ProcessBuilder(); + processBuilder.command(command); + processBuilder.redirectErrorStream(true); + + Process process = processBuilder.start(); + //Process process = Runtime.getRuntime().exec(command); + + BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); + bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); + + // command log + String line; + while ((line = bufferedReader.readLine()) != null) { + XxlJobHelper.log(line); + } + + // command exit + process.waitFor(); + exitValue = process.exitValue(); + } catch (Exception e) { + XxlJobHelper.log(e); + } finally { + if (bufferedReader != null) { + bufferedReader.close(); + } + } + + if (exitValue == 0) { + // default success + } else { + XxlJobHelper.handleFail("command exit value(" + exitValue + ") is failed"); + } + + } + + + /** + * 4、跨平台Http任务 + * 参数示例: + * "url: http://www.baidu.com\n" + + * "method: get\n" + + * "data: content\n"; + */ + @XxlJob("httpJobHandler") + public void httpJobHandler() throws Exception { + + // param parse + String param = XxlJobHelper.getJobParam(); + if (param == null || param.trim().length() == 0) { + XxlJobHelper.log("param[" + param + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + + String[] httpParams = param.split("\n"); + String url = null; + String method = null; + String data = null; + for (String httpParam : httpParams) { + if (httpParam.startsWith("url:")) { + url = httpParam.substring(httpParam.indexOf("url:") + 4).trim(); + } + if (httpParam.startsWith("method:")) { + method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase(); + } + if (httpParam.startsWith("data:")) { + data = httpParam.substring(httpParam.indexOf("data:") + 5).trim(); + } + } + + // param valid + if (url == null || url.trim().length() == 0) { + XxlJobHelper.log("url[" + url + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + if (method == null || !Arrays.asList("GET", "POST").contains(method)) { + XxlJobHelper.log("method[" + method + "] invalid."); + + XxlJobHelper.handleFail(); + return; + } + boolean isPostMethod = method.equals("POST"); + + // request + HttpURLConnection connection = null; + BufferedReader bufferedReader = null; + try { + // connection + URL realUrl = new URL(url); + connection = (HttpURLConnection) realUrl.openConnection(); + + // connection setting + connection.setRequestMethod(method); + connection.setDoOutput(isPostMethod); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setReadTimeout(5 * 1000); + connection.setConnectTimeout(3 * 1000); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); + connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); + + // do connection + connection.connect(); + + // data + if (isPostMethod && data != null && data.trim().length() > 0) { + DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); + dataOutputStream.write(data.getBytes("UTF-8")); + dataOutputStream.flush(); + dataOutputStream.close(); + } + + // valid StatusCode + int statusCode = connection.getResponseCode(); + if (statusCode != 200) { + throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); + } + + // result + bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + StringBuilder result = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + result.append(line); + } + String responseMsg = result.toString(); + + XxlJobHelper.log(responseMsg); + + return; + } catch (Exception e) { + XxlJobHelper.log(e); + + XxlJobHelper.handleFail(); + return; + } finally { + try { + if (bufferedReader != null) { + bufferedReader.close(); + } + if (connection != null) { + connection.disconnect(); + } + } catch (Exception e2) { + XxlJobHelper.log(e2); + } + } + + } + + /** + * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; + */ + @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") + public void demoJobHandler2() throws Exception { + XxlJobHelper.log("XXL-JOB, Hello World."); + } + + public void init() { + logger.info("init"); + } + + public void destroy() { + logger.info("destroy"); + } + + +} diff --git a/blade-example/blade-xxl-job/src/main/resources/application.yml b/blade-example/blade-xxl-job/src/main/resources/application.yml new file mode 100644 index 0000000..cd4a436 --- /dev/null +++ b/blade-example/blade-xxl-job/src/main/resources/application.yml @@ -0,0 +1,25 @@ +server: + port: 7008 + +logging: + config: classpath:logback.xml + +spring: + main: + allow-circular-references: true + +xxl: + job: + accessToken: default_token + admin: + addresses: http://127.0.0.1:8080/xxl-job-admin + executor: + # 执行器名称 + appname: blade-xxl-job-executor + # 若使用docker部署xxl-job-admin并且本地开发机调试连接,需要开启指定ip,采用docker的internal配置 + # 若生产部署不涉及跨网段,可不配置ip + ip: 'http://host.docker.internal' + port: 9999 + logpath: ../data/applogs/xxl-job/jobhandler + logretentiondays: -1 + address: '' diff --git a/blade-example/blade-xxl-job/src/main/resources/logback.xml b/blade-example/blade-xxl-job/src/main/resources/logback.xml new file mode 100644 index 0000000..43b344f --- /dev/null +++ b/blade-example/blade-xxl-job/src/main/resources/logback.xml @@ -0,0 +1,40 @@ + + + + logback + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + utf8 + + + + + ${log.path} + + ${log.path}.%d{yyyy-MM-dd}.zip + + + %date %level [%thread] %logger{36} [%file : %line] %msg%n + + + + + + + + + + diff --git a/blade-example/pom.xml b/blade-example/pom.xml new file mode 100644 index 0000000..086848b --- /dev/null +++ b/blade-example/pom.xml @@ -0,0 +1,36 @@ + + + + BladeX-Biz + org.springblade + ${revision} + + 4.0.0 + + blade-example + ${project.artifactId} + pom + BladeX 微服务范例集合 + + + blade-apollo + blade-mqtt-broker + blade-mqtt-client + blade-rabbit-listener + blade-rabbit-publisher + blade-seata-order + blade-seata-storage + blade-sharding-jdbc + blade-xxl-job + + + + + org.springblade + blade-biz-common + + + + diff --git a/blade-gateway/Dockerfile b/blade-gateway/Dockerfile new file mode 100644 index 0000000..50dcd37 --- /dev/null +++ b/blade-gateway/Dockerfile @@ -0,0 +1,15 @@ +FROM bladex/alpine-java:openjdk17_cn_slim + +LABEL maintainer="bladejava@qq.com" + +RUN mkdir -p /blade/gateway + +WORKDIR /blade/gateway + +EXPOSE 80 + +COPY ./target/blade-gateway.jar ./app.jar + +ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] + +CMD ["--spring.profiles.active=test"] diff --git a/blade-gateway/pom.xml b/blade-gateway/pom.xml new file mode 100644 index 0000000..48d764c --- /dev/null +++ b/blade-gateway/pom.xml @@ -0,0 +1,103 @@ + + + + BladeX-Biz + org.springblade + ${revision} + + 4.0.0 + + blade-gateway + ${project.artifactId} + jar + + + + + org.springblade + blade-core-launch + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-undertow + + + + + org.springblade + blade-biz-common + + + org.springblade + blade-core-launch + + + + + org.springblade + blade-starter-metrics + + + org.springblade + blade-starter-jwt + + + com.alibaba + fastjson + + + + org.springframework.cloud + spring-cloud-starter-gateway-server-webflux + + + org.springframework.cloud + spring-cloud-starter-bootstrap + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + org.springframework.boot + spring-boot-starter-data-redis-reactive + + + de.codecentric + spring-boot-admin-starter-client + + + + com.github.xiaoymin + knife4j-gateway-spring-boot-starter + + + + + + + + + io.fabric8 + docker-maven-plugin + + ${docker.fabric.skip} + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + diff --git a/blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java b/blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java new file mode 100644 index 0000000..a724b7c --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/GateWayApplication.java @@ -0,0 +1,48 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway; + +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.launch.constant.AppConstant; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 项目启动 + * + * @author Chill + */ +@EnableScheduling +@EnableDiscoveryClient +@SpringBootApplication +public class GateWayApplication { + + public static void main(String[] args) { + BladeApplication.run(AppConstant.APPLICATION_GATEWAY_NAME, GateWayApplication.class, args); + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java b/blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java new file mode 100644 index 0000000..50e8d1a --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/config/ErrorHandlerConfiguration.java @@ -0,0 +1,53 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.config; + + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.springblade.gateway.handler.ErrorExceptionHandler; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +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.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 异常处理配置类 + * + * @author Chill + */ +@Configuration(proxyBeanMethods = false) +@AutoConfigureBefore(ErrorWebFluxAutoConfiguration.class) +@EnableConfigurationProperties({ServerProperties.class}) +public class ErrorHandlerConfiguration { + + @Bean + public ErrorExceptionHandler globalExceptionHandler(ObjectMapper objectMapper) { + return new ErrorExceptionHandler(objectMapper); + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/config/ReactiveDiscoveryConfiguration.java b/blade-gateway/src/main/java/org/springblade/gateway/config/ReactiveDiscoveryConfiguration.java new file mode 100644 index 0000000..0b3e724 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/config/ReactiveDiscoveryConfiguration.java @@ -0,0 +1,50 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.config; + +import com.github.xiaoymin.knife4j.spring.gateway.Knife4jGatewayProperties; +import com.github.xiaoymin.knife4j.spring.gateway.discover.router.DiscoverClientRouteServiceConvert; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * knife4j自动聚合配置 + * + * @author BladeX + */ +@Configuration(proxyBeanMethods = false) +public class ReactiveDiscoveryConfiguration { + + @Bean + @ConditionalOnProperty(name = {"spring.cloud.gateway.server.webflux.discovery.locator.enabled"}) + public DiscoverClientRouteServiceConvert discoverClientRouteServiceConvert(DiscoveryClientRouteDefinitionLocator discoveryClient, + Knife4jGatewayProperties knife4jGatewayProperties) { + return new DiscoverClientRouteServiceConvert(discoveryClient, knife4jGatewayProperties); + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java b/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java new file mode 100644 index 0000000..4ad7d58 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java @@ -0,0 +1,57 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.config; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.gateway.filter.GatewayFilter; +import org.springblade.gateway.props.AuthProperties; +import org.springblade.gateway.props.RequestProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.server.WebFilter; + +/** + * 路由配置信息 + * + * @author Chill + */ +@Slf4j +@Configuration(proxyBeanMethods = false) +@AllArgsConstructor +@EnableConfigurationProperties({AuthProperties.class, RequestProperties.class}) +public class RouterFunctionConfiguration { + + /** + * 全局配置 + */ + @Bean + public WebFilter gatewayFilter(RequestProperties requestProperties) { + return new GatewayFilter(requestProperties); + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java new file mode 100644 index 0000000..d2ca11b --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteService.java @@ -0,0 +1,117 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 routeDefinitions) { + try { + if (!routeDefinitions.isEmpty()) { + routeDefinitions.forEach(this::update); + } + return "update done"; + } catch (Exception e) { + e.printStackTrace(); + return "update failure"; + } + } + + /** + * 删除路由 + */ + public String delete(String id) { + try { + this.routeDefinitionWriter.delete(Mono.just(id)); + return "delete success"; + } catch (Exception e) { + e.printStackTrace(); + return "delete failure"; + } + } + + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java new file mode 100644 index 0000000..0671cea --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/DynamicRouteServiceListener.java @@ -0,0 +1,109 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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.context.config.annotation.RefreshScope; +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 +@RefreshScope +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 = 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()); + properties.setProperty(PropertyKeyConst.USERNAME, nacosDiscoveryProperties.getUsername()); + properties.setProperty(PropertyKeyConst.PASSWORD, nacosDiscoveryProperties.getPassword()); + ConfigService configService = NacosFactory.createConfigService(properties); + configService.addListener(dataId, group, new Listener() { + @Override + public void receiveConfigInfo(String configInfo) { + List routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class); + if (!routeDefinitions.isEmpty()) { + dynamicRouteService.updateList(routeDefinitions); + } + } + + @Override + public Executor getExecutor() { + return null; + } + }); + String configInfo = configService.getConfig(dataId, group, 5000); + if (configInfo != null) { + List routeDefinitions = JSON.parseArray(configInfo, RouteDefinition.class); + dynamicRouteService.updateList(routeDefinitions); + } + } catch (NacosException ignored) { + ignored.printStackTrace(); + } + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java new file mode 100644 index 0000000..1eccac9 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayFilter.java @@ -0,0 +1,50 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 args = new LinkedHashMap<>(); +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java new file mode 100644 index 0000000..39681ff --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayPredicate.java @@ -0,0 +1,50 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 args = new LinkedHashMap<>(); +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java new file mode 100644 index 0000000..b1d74ef --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/dynamic/GatewayRoute.java @@ -0,0 +1,66 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 predicates = new ArrayList<>(); + + /** + * 路由过滤器集合配置 + */ + private List filters = new ArrayList<>(); + + /** + * 路由规则转发的目标uri + */ + private String uri; + + /** + * 路由执行的顺序 + */ + private int order = 0; +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java new file mode 100644 index 0000000..950e967 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/filter/AuthFilter.java @@ -0,0 +1,156 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.filter; + +import com.alibaba.nacos.common.utils.StringUtils; +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.springblade.core.jwt.JwtCrypto; +import org.springblade.core.jwt.JwtUtil; +import org.springblade.core.jwt.props.JwtProperties; +import org.springblade.core.launch.constant.TokenConstant; +import org.springblade.core.launch.props.BladeProperties; +import org.springblade.gateway.props.AuthProperties; +import org.springblade.gateway.provider.AuthProvider; +import org.springblade.gateway.provider.KeyProvider; +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.util.AntPathMatcher; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.nio.charset.StandardCharsets; + +import static org.springblade.core.jwt.JwtCrypto.BLADE_TOKEN_CRYPTO_KEY; + +/** + * 鉴权认证 + * + * @author Chill + */ +@Slf4j +@Component +@AllArgsConstructor +public class AuthFilter implements GlobalFilter, Ordered { + private static final String MSG_TOKEN_MISSING = "缺失令牌,鉴权失败"; + private static final String MSG_REQUEST_UNAUTHORIZED = "请求未授权"; + private static final String MSG_TOKEN_EXPIRED = "令牌已失效"; + + private final AuthProperties authProperties; + private final ObjectMapper objectMapper; + private final JwtProperties jwtProperties; + private final BladeProperties bladeProperties; + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + @Override + public Mono 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.isBlank(headerToken) && StringUtils.isBlank(paramToken)) { + return unAuth(resp, MSG_TOKEN_MISSING); + } + String auth = StringUtils.isBlank(headerToken) ? paramToken : headerToken; + // 判断 auth 是否为空 + if (StringUtils.isBlank(auth)) { + return unAuth(resp, MSG_REQUEST_UNAUTHORIZED); + } + // 校验 超级密钥 合法性,剩余属性交由微服务内部自行鉴权 + if (KeyProvider.isApiKey(auth, bladeProperties)) { + return chain.filter(exchange); + } + String token = JwtUtil.getToken(auth); + // 判断 token 是否为空 + if (StringUtils.isBlank(token)) { + return unAuth(resp, MSG_REQUEST_UNAUTHORIZED); + } + // 校验 加密Token 合法性 + if (JwtUtil.isCrypto(auth)) { + token = JwtCrypto.decryptToString(token, bladeProperties.getEnvironment().getProperty(BLADE_TOKEN_CRYPTO_KEY)); + } + Claims claims = JwtUtil.parseJWT(token); + if (token == null || claims == null) { + return unAuth(resp, MSG_REQUEST_UNAUTHORIZED); + } + // 判断 Token 状态 + if (jwtProperties.getState()) { + String tenantId = String.valueOf(claims.get(TokenConstant.TENANT_ID)); + String clientId = String.valueOf(claims.get(TokenConstant.CLIENT_ID)); + String userId = String.valueOf(claims.get(TokenConstant.USER_ID)); + String accessToken = JwtUtil.getAccessToken(tenantId, clientId, userId, token); + if (!token.equalsIgnoreCase(accessToken)) { + return unAuth(resp, MSG_TOKEN_EXPIRED); + } + } + return chain.filter(exchange); + } + + private boolean isSkip(String path) { + return AuthProvider.getDefaultSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)) + || authProperties.getSkipUrl().stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)) + || authProperties.getAuth().stream().anyMatch(auth -> antPathMatcher.match(auth.getPattern(), path)) + || authProperties.getBasic().stream().anyMatch(basic -> antPathMatcher.match(basic.getPattern(), path)) + || authProperties.getSign().stream().anyMatch(sign -> antPathMatcher.match(sign.getPattern(), path)); + } + + private Mono 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; + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java new file mode 100644 index 0000000..953f52b --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java @@ -0,0 +1,171 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.filter; + +import lombok.RequiredArgsConstructor; +import org.springblade.gateway.props.RequestProperties; +import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.lang.NonNull; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PatternMatchUtils; +import org.springframework.web.cors.reactive.CorsUtils; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Objects; + +/** + * 全局拦截器 + * + * @author Chill + */ +@RequiredArgsConstructor +public class GatewayFilter implements WebFilter, Ordered { + + /** + * 请求配置 + */ + private final RequestProperties requestProperties; + /** + * 路径匹配 + */ + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + /** + * 默认拦截地址 + */ + private final List defaultBlockUrl = List.of("/**/actuator/**", "/health/**"); + /** + * 默认白名单 + */ + private final List defaultWhiteList = List.of("127.0.0.1", "172.30.*.*", "192.168.*.*", "10.*.*.*", "0:0:0:0:0:0:0:1"); + /** + * 默认提示信息 + */ + private final static String DEFAULT_MESSAGE = "当前请求被拒绝,请联系管理员!"; + + /** + * 这里为支持的请求头,如果有自定义的header字段请自己添加 + */ + private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, knife4j-gateway-code, request-origion"; + 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"; + + + @NonNull + @Override + public Mono filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + // 处理跨域请求 + if (CorsUtils.isCorsRequest(request)) { + ServerHttpResponse response = exchange.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(); + } + } + // 处理黑白名单与拦截请求 + if (requestProperties.getEnabled()) { + String path = request.getPath().value(); + String ip = Objects.requireNonNull(request.getRemoteAddress()).getHostString(); + if (isRequestBlock(path, ip)) { + throw new RuntimeException(DEFAULT_MESSAGE); + } + } + return chain.filter(exchange); + } + + + /** + * 是否白名单 + * + * @param ip ip地址 + * @return boolean + */ + private boolean isWhiteList(String ip) { + List whiteList = requestProperties.getWhiteList(); + String[] defaultWhiteIps = defaultWhiteList.toArray(new String[0]); + String[] whiteIps = whiteList.toArray(new String[0]); + return PatternMatchUtils.simpleMatch(defaultWhiteIps, ip) || PatternMatchUtils.simpleMatch(whiteIps, ip); + } + + /** + * 是否黑名单 + * + * @param ip ip地址 + * @return boolean + */ + private boolean isBlackList(String ip) { + List blackList = requestProperties.getBlackList(); + String[] blackIps = blackList.toArray(new String[0]); + return PatternMatchUtils.simpleMatch(blackIps, ip); + } + + /** + * 是否禁用请求访问 + * + * @param path 请求路径 + * @return boolean + */ + private boolean isRequestBlock(String path) { + List blockUrl = requestProperties.getBlockUrl(); + return defaultBlockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)) || + blockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)); + } + + /** + * 是否拦截请求 + * + * @param path 请求路径 + * @param ip ip地址 + * @return boolean + */ + private boolean isRequestBlock(String path, String ip) { + return (isRequestBlock(path) && !isWhiteList(ip)) || isBlackList(ip); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestFilter.java new file mode 100644 index 0000000..b351716 --- /dev/null +++ b/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; + +/** + *

+ * 全局拦截器,作用所有的微服务 + *

+ * 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} to indicate when request processing is complete + */ + @Override + public Mono 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; + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestLogFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestLogFilter.java new file mode 100644 index 0000000..910516f --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/filter/RequestLogFilter.java @@ -0,0 +1,121 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: DreamLu (596392912@qq.com) + */ +package org.springblade.gateway.filter; + +import com.alibaba.nacos.common.utils.StringUtils; +import io.jsonwebtoken.Claims; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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 日志请求记录,方便开发调试。请求日志过滤器排序尽量低。 + * + *

+ * 注意:暂时不支持结构体打印,想实现,请看下面的链接。 + * 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 + *

+ * + * @author dream.lu + */ +@Slf4j +@Configuration(proxyBeanMethods = false) +@RequiredArgsConstructor +@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true) +public class RequestLogFilter implements GlobalFilter, Ordered { + private final WebEndpointProperties endpointProperties; + + @Override + public Mono 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 beforeReqArgs = new ArrayList<>(); + beforeReqLog.append("\n\n================ Gateway Request Start ================\n"); + // 打印路由 + beforeReqLog.append("===> {}: {}\n"); + // 参数 + String requestMethod = request.getMethod().name(); + 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(headerValue.toArray()); + } else { + beforeReqArgs.add(headerValue.toArray()); + } + }); + + beforeReqLog.append("================ Gateway Request End =================\n"); + // 打印执行时间 + log.info(beforeReqLog.toString(), beforeReqArgs.toArray()); + return chain.filter(exchange); + } + + @Override + public int getOrder() { + return Ordered.LOWEST_PRECEDENCE; + } +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/ResponseLogFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/ResponseLogFilter.java new file mode 100644 index 0000000..27dd5fe --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/filter/ResponseLogFilter.java @@ -0,0 +1,108 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: DreamLu (596392912@qq.com) + */ +package org.springblade.gateway.filter; + +import com.alibaba.nacos.common.utils.StringUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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(proxyBeanMethods = false) +@RequiredArgsConstructor +@ConditionalOnProperty(value = "blade.log.request.enabled", havingValue = "true", matchIfMissing = true) +public class ResponseLogFilter implements GlobalFilter, Ordered { + private final WebEndpointProperties endpointProperties; + + @Override + public Mono 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 queryParams = request.getQueryParams(); + String requestUrl = UriComponentsBuilder.fromPath(path).queryParams(queryParams).build().toUriString(); + + // 构建成一条长 日志,避免并发下日志错乱 + StringBuilder responseLog = new StringBuilder(300); + // 日志参数 + List 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.getMethod().name(); + 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(headerValue.toArray()); + }); + + responseLog.append("================ Gateway Response End =================\n"); + // 打印执行时间 + log.info(responseLog.toString(), responseArgs.toArray()); + }) + ); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java b/blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java new file mode 100644 index 0000000..a839204 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/handler/ErrorExceptionHandler.java @@ -0,0 +1,101 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.handler; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.springblade.gateway.provider.ResponseProvider; +import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler; +import org.springframework.core.annotation.Order; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.MediaType; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.web.server.ResponseStatusException; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.Map; + +/** + * 异常处理 + * + * @author Chill + */ +@Order(-1) +@RequiredArgsConstructor +public class ErrorExceptionHandler implements ErrorWebExceptionHandler { + + private final ObjectMapper objectMapper; + + @Override + public Mono handle(ServerWebExchange exchange, Throwable ex) { + ServerHttpRequest request = exchange.getRequest(); + ServerHttpResponse response = exchange.getResponse(); + + if (response.isCommitted()) { + return Mono.error(ex); + } + + response.getHeaders().setContentType(MediaType.APPLICATION_JSON); + if (ex instanceof ResponseStatusException) { + response.setStatusCode(((ResponseStatusException) ex).getStatusCode()); + } + + return response.writeWith(Mono.fromSupplier(() -> { + DataBufferFactory bufferFactory = response.bufferFactory(); + try { + int status = 500; + if (response.getStatusCode() != null) { + status = response.getStatusCode().value(); + } + Map result = ResponseProvider.response(status, this.buildMessage(request, ex)); + return bufferFactory.wrap(objectMapper.writeValueAsBytes(result)); + } catch (JsonProcessingException e) { + return bufferFactory.wrap(new byte[0]); + } + })); + } + + + /** + * 构建异常信息 + */ + private String buildMessage(ServerHttpRequest request, Throwable ex) { + StringBuilder message = new StringBuilder("Failed to handle request ["); + message.append(request.getMethod().name()); + message.append(" "); + message.append(request.getURI()); + message.append("]"); + if (ex != null) { + message.append(": "); + message.append(ex.getMessage()); + } + return message.toString(); + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java b/blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java new file mode 100644 index 0000000..523213f --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/props/AuthProperties.java @@ -0,0 +1,68 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.props; + +import lombok.Data; +import org.springblade.gateway.provider.AuthSecure; +import org.springblade.gateway.provider.BasicSecure; +import org.springblade.gateway.provider.SignSecure; +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 skipUrl = new ArrayList<>(); + + /** + * 自定义授权配置 + */ + private final List auth = new ArrayList<>(); + + /** + * 基础认证配置 + */ + private final List basic = new ArrayList<>(); + + /** + * 签名认证配置 + */ + private final List sign = new ArrayList<>(); + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java b/blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java new file mode 100644 index 0000000..14eafa3 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java @@ -0,0 +1,68 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.props; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * Request配置类 + * + * @author Chill + */ +@Data +@ConfigurationProperties("blade.request") +public class RequestProperties { + + /** + * 开启自定义request + */ + private Boolean enabled = true; + + /** + * 放行url + */ + private List skipUrl = new ArrayList<>(); + + /** + * 禁用url + */ + private List blockUrl = new ArrayList<>(); + + /** + * 白名单,支持通配符,例如:10.20.0.8*、10.20.0.* + */ + private List whiteList = new ArrayList<>(); + + /** + * 黑名单,支持通配符,例如:10.20.0.8*、10.20.0.* + */ + private List blackList = new ArrayList<>(); + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java new file mode 100644 index 0000000..75aeb18 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthProvider.java @@ -0,0 +1,71 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 AUTH_KEY = TokenConstant.AUTH_HEADER; + private static final List 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/sms/**"); + 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/**"); + DEFAULT_SKIP_URL.add("/v3/api-docs/**"); + 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("/assets/**"); + } + + /** + * 默认无需鉴权的API + */ + public static List getDefaultSkipUrl() { + return DEFAULT_SKIP_URL; + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthSecure.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthSecure.java new file mode 100644 index 0000000..bf2cbf5 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/AuthSecure.java @@ -0,0 +1,46 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.provider; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 自定义授权规则 + * + * @author Chill + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AuthSecure { + /** + * 请求路径 + */ + private String pattern; + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/BasicSecure.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/BasicSecure.java new file mode 100644 index 0000000..3b466e0 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/BasicSecure.java @@ -0,0 +1,46 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.provider; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 基础授权规则 + * + * @author Chill + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BasicSecure { + /** + * 请求路径 + */ + private String pattern; + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/KeyProvider.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/KeyProvider.java new file mode 100644 index 0000000..63f982a --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/KeyProvider.java @@ -0,0 +1,303 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.provider; + +import org.springblade.core.launch.props.BladeProperties; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Objects; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 超级密钥加解密工具类 + * + * @author BladeX + */ +public class KeyProvider { + public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; + + /** + * API Key 前缀 + */ + private static final String API_KEY_PREFIX = "ak-"; + /** + * API Key 配置项 + */ + private static final String BLADE_KEY_ENABLED = "blade.key.enabled"; + /** + * API Key 配置项 + */ + private static final String BLADE_KEY_CRYPTO_KEY = "blade.key.crypto-key"; + /** + * 随机字符串因子 + */ + private static final String RANDOM_FACTOR = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + /** + * 安全随机数生成器 + */ + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + /** + * PKCS7 块大小 + */ + private static final int BLOCK_SIZE = 16; + /** + * Hex 编码字符 + */ + private static final byte[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + /** + * 判断是否为合法 API Key + * + * @param auth 认证字符串 + * @return 是否为 API Key + */ + public static boolean isApiKey(@Nullable String auth, BladeProperties bladeProperties) { + // 检查功能是否启用 + String enabled = bladeProperties.getEnvironment().getProperty(BLADE_KEY_ENABLED); + if (!Boolean.parseBoolean(enabled)) { + return false; + } + // 检查前缀并解析 + if (auth != null && auth.startsWith(API_KEY_PREFIX)) { + String cryptoKey = bladeProperties.getEnvironment().getProperty(BLADE_KEY_CRYPTO_KEY); + String parsed = parseKey(auth, cryptoKey); + return parsed != null; + } + return false; + } + + /** + * 生成 API Key + * + * @param cryptoKey 加密密钥 + * @return 带前缀的加密 Key + */ + public static String generateKey(String cryptoKey) { + return API_KEY_PREFIX + encryptToHex(randomUUID(), cryptoKey); + } + + /** + * 解析 API Key + * + * @param key 带前缀的加密 Key + * @param cryptoKey 加密密钥 + * @return 解密后的原始值 + */ + @Nullable + public static String parseKey(@Nullable String key, String cryptoKey) { + if (isBlank(key) || !key.startsWith(API_KEY_PREFIX)) { + return null; + } + try { + String encryptedPart = key.substring(API_KEY_PREFIX.length()); + return decryptFormHexToString(encryptedPart, cryptoKey); + } catch (Exception e) { + return null; + } + } + + /** + * 加密并转换为十六进制字符串 + * + * @param content 明文内容 + * @param aesTextKey AES 密钥字符串 + * @return 十六进制加密字符串 + */ + public static String encryptToHex(String content, String aesTextKey) { + return hexEncode(encrypt(content.getBytes(DEFAULT_CHARSET), aesTextKey)); + } + + /** + * 从十六进制字符串解密并返回明文字符串 + * + * @param content 十六进制加密字符串 + * @param aesTextKey AES 密钥字符串 + * @return 解密后的明文字符串 + */ + @Nullable + public static String decryptFormHexToString(@Nullable String content, String aesTextKey) { + byte[] hexBytes = decryptFormHex(content, aesTextKey); + if (hexBytes == null) { + return null; + } + return new String(hexBytes, DEFAULT_CHARSET); + } + + /** + * 从十六进制字符串解密并返回字节数组 + * + * @param content 十六进制加密字符串 + * @param aesTextKey AES 密钥字符串 + * @return 解密后的字节数组 + */ + @Nullable + public static byte[] decryptFormHex(@Nullable String content, String aesTextKey) { + if (isBlank(content)) { + return null; + } + return decrypt(hexDecode(content.getBytes(DEFAULT_CHARSET)), aesTextKey); + } + + /** + * 加密字节数组 + * + * @param content 明文字节数组 + * @param aesTextKey AES 密钥字符串 + * @return 加密后的字节数组 + */ + public static byte[] encrypt(byte[] content, String aesTextKey) { + return aes(pkcs7Encode(content), Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET), Cipher.ENCRYPT_MODE); + } + + /** + * 解密字节数组 + * + * @param content 加密的字节数组 + * @param aesTextKey AES 密钥字符串 + * @return 解密后的字节数组 + */ + public static byte[] decrypt(byte[] content, String aesTextKey) { + return pkcs7Decode(aes(content, Objects.requireNonNull(aesTextKey).getBytes(DEFAULT_CHARSET), Cipher.DECRYPT_MODE)); + } + + /** + * AES 加解密核心方法 + */ + private static byte[] aes(byte[] data, byte[] aesKey, int mode) { + Assert.isTrue(aesKey.length == 32, "IllegalAesKey, aesKey's length must be 32"); + try { + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES"); + IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16)); + cipher.init(mode, keySpec, iv); + return cipher.doFinal(data); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + // ==================== 内联工具方法 ==================== + + /** + * 生成指定长度的随机字符串 + */ + public static String randomKey(int count) { + char[] buffer = new char[count]; + for (int i = 0; i < count; i++) { + buffer[i] = RANDOM_FACTOR.charAt(SECURE_RANDOM.nextInt(RANDOM_FACTOR.length())); + } + return new String(buffer); + } + + /** + * 生成无横线的 UUID + */ + private static String randomUUID() { + ThreadLocalRandom random = ThreadLocalRandom.current(); + return new UUID(random.nextLong(), random.nextLong()).toString().replace("-", ""); + } + + /** + * 检查字符串是否为空或空白 + */ + private static boolean isBlank(@Nullable String str) { + if (str == null || str.isEmpty()) { + return true; + } + for (int i = 0; i < str.length(); i++) { + if (!Character.isWhitespace(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + * 字节数组转十六进制字符串 + */ + private static String hexEncode(byte[] data) { + int len = data.length; + byte[] out = new byte[len << 1]; + for (int i = 0, j = 0; i < len; i++) { + out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4]; + out[j++] = HEX_DIGITS[0x0F & data[i]]; + } + return new String(out, DEFAULT_CHARSET); + } + + /** + * 十六进制字节数组解码为原始字节数组 + */ + private static byte[] hexDecode(byte[] data) { + int len = data.length; + if ((len & 0x01) != 0) { + throw new IllegalArgumentException("hexBinary needs to be even-length: " + len); + } + byte[] out = new byte[len >> 1]; + for (int i = 0, j = 0; j < len; i++) { + int f = Character.digit(data[j++], 16) << 4; + f |= Character.digit(data[j++], 16); + out[i] = (byte) (f & 0xFF); + } + return out; + } + + /** + * PKCS7 编码(填充) + */ + private static byte[] pkcs7Encode(byte[] src) { + int count = src.length; + int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE); + byte pad = (byte) (amountToPad & 0xFF); + byte[] dest = new byte[count + amountToPad]; + System.arraycopy(src, 0, dest, 0, count); + Arrays.fill(dest, count, dest.length, pad); + return dest; + } + + /** + * PKCS7 解码(去填充) + */ + private static byte[] pkcs7Decode(byte[] decrypted) { + int pad = decrypted[decrypted.length - 1]; + if (pad < 1 || pad > BLOCK_SIZE) { + pad = 0; + } + if (pad > 0) { + return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad); + } + return decrypted; + } +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java new file mode 100644 index 0000000..4415269 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/RequestProvider.java @@ -0,0 +1,58 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 uris = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR); + URI requestUri = uris.stream().findFirst().orElse(request.getURI()); + MultiValueMap queryParams = request.getQueryParams(); + return UriComponentsBuilder.fromPath(requestUri.getRawPath()).queryParams(queryParams).build().toUriString(); + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java new file mode 100644 index 0000000..f820603 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/ResponseProvider.java @@ -0,0 +1,93 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.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 success(String message) { + return response(200, message); + } + + /** + * 失败 + * + * @param message 信息 + * @return + */ + public static Map fail(String message) { + return response(400, message); + } + + /** + * 未授权 + * + * @param message 信息 + * @return + */ + public static Map unAuth(String message) { + return response(401, message); + } + + /** + * 服务器异常 + * + * @param message 信息 + * @return + */ + public static Map error(String message) { + return response(500, message); + } + + /** + * 构建返回的JSON数据格式 + * + * @param status 状态码 + * @param message 信息 + * @return + */ + public static Map response(int status, String message) { + Map map = new HashMap<>(16); + map.put("code", status); + map.put("msg", message); + map.put("data", null); + return map; + } + +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/provider/SignSecure.java b/blade-gateway/src/main/java/org/springblade/gateway/provider/SignSecure.java new file mode 100644 index 0000000..4168671 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/provider/SignSecure.java @@ -0,0 +1,46 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.gateway.provider; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 签名授权规则 + * + * @author Chill + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SignSecure { + /** + * 请求路径 + */ + private String pattern; + +} diff --git a/blade-gateway/src/main/resources/application-dev.yml b/blade-gateway/src/main/resources/application-dev.yml new file mode 100644 index 0000000..b8eaf54 --- /dev/null +++ b/blade-gateway/src/main/resources/application-dev.yml @@ -0,0 +1,11 @@ +blade: + #多团队协作服务配置 + loadbalancer: + #开启配置 + enabled: true + #灰度版本 + #version: 3.0.0 + #负载均衡优先调用的ip段 + prior-ip-pattern: + - 192.168.0.* + - 127.0.0.1 diff --git a/blade-gateway/src/main/resources/application-prod.yml b/blade-gateway/src/main/resources/application-prod.yml new file mode 100644 index 0000000..b8eaf54 --- /dev/null +++ b/blade-gateway/src/main/resources/application-prod.yml @@ -0,0 +1,11 @@ +blade: + #多团队协作服务配置 + loadbalancer: + #开启配置 + enabled: true + #灰度版本 + #version: 3.0.0 + #负载均衡优先调用的ip段 + prior-ip-pattern: + - 192.168.0.* + - 127.0.0.1 diff --git a/blade-gateway/src/main/resources/application-test.yml b/blade-gateway/src/main/resources/application-test.yml new file mode 100644 index 0000000..b8eaf54 --- /dev/null +++ b/blade-gateway/src/main/resources/application-test.yml @@ -0,0 +1,11 @@ +blade: + #多团队协作服务配置 + loadbalancer: + #开启配置 + enabled: true + #灰度版本 + #version: 3.0.0 + #负载均衡优先调用的ip段 + prior-ip-pattern: + - 192.168.0.* + - 127.0.0.1 diff --git a/blade-gateway/src/main/resources/application.yml b/blade-gateway/src/main/resources/application.yml new file mode 100644 index 0000000..d0c3371 --- /dev/null +++ b/blade-gateway/src/main/resources/application.yml @@ -0,0 +1,16 @@ +knife4j: + gateway: + enabled: true + tags-sorter: order + operations-sorter: order + # 指定服务发现的模式聚合微服务文档,并且是默认`default`分组 + strategy: discover + discover: + enabled: true + # 指定版本号(Swagger2|OpenAPI3) + version : openapi3 + # 需要排除的微服务(eg:网关服务) + excluded-services: + - blade-admin + - blade-gateway + - blade-log diff --git a/blade-gateway/src/main/resources/bootstrap.yml b/blade-gateway/src/main/resources/bootstrap.yml new file mode 100644 index 0000000..15511b1 --- /dev/null +++ b/blade-gateway/src/main/resources/bootstrap.yml @@ -0,0 +1,39 @@ +server: + port: 80 + +spring: + cloud: + nacos: + config: + server-addr: 127.0.0.1:8848 + namespace: + group: DEFAULT_GROUP + username: nacos + password: nacos + discovery: + server-addr: 127.0.0.1:8848 + namespace: + group: DEFAULT_GROUP + username: nacos + password: nacos + gateway: + discovery: + locator: + enabled: true + loadbalancer: + retry: + enabled: true + +# 本地兜底:JWT token 签名密钥 +blade: + token: + sign-key: 8rnuOcKSlxfe1TcwXVzw8x4VQtB3PG08 + +# 兼容性说明:新版gateway配置需要最后启动才能发现服务,否则会出现404的问题 +# 此问题可等spring官方修复,或临时处理方案采用下方旧版配置即可自动发现服务 +#spring: +# cloud: +# gateway: +# discovery: +# locator: +# enabled: true diff --git a/blade-service-api/blade-demo-api/pom.xml b/blade-service-api/blade-demo-api/pom.xml new file mode 100644 index 0000000..9c9ac21 --- /dev/null +++ b/blade-service-api/blade-demo-api/pom.xml @@ -0,0 +1,16 @@ + + + + blade-service-api + org.springblade + ${revision} + + 4.0.0 + + blade-demo-api + ${project.artifactId} + jar + + diff --git a/blade-service-api/blade-demo-api/src/main/java/com/example/demo/feign/INoticeClient.java b/blade-service-api/blade-demo-api/src/main/java/com/example/demo/feign/INoticeClient.java new file mode 100644 index 0000000..1529a54 --- /dev/null +++ b/blade-service-api/blade-demo-api/src/main/java/com/example/demo/feign/INoticeClient.java @@ -0,0 +1,59 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.feign; + +import org.springblade.core.launch.constant.AppConstant; +import org.springblade.core.tool.api.R; +import com.example.demo.pojo.entity.Notice; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +/** + * Notice Feign接口类 + * + * @author Chill + */ +@FeignClient( + value = AppConstant.APPLICATION_DESK_NAME +) +public interface INoticeClient { + + String API_PREFIX = "/client"; + String TOP = API_PREFIX + "/top"; + + /** + * 获取notice列表 + * + * @param number + * @return + */ + @GetMapping(TOP) + R> top(@RequestParam("number") Integer number); + +} diff --git a/blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/entity/Notice.java b/blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/entity/Notice.java new file mode 100644 index 0000000..3a9d2ed --- /dev/null +++ b/blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/entity/Notice.java @@ -0,0 +1,75 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.pojo.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.mp.base.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 实体类 + * + * @author Chill + */ +@Data +@TableName("blade_notice") +@EqualsAndHashCode(callSuper = true) +public class Notice extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 标题 + */ + @Schema(description = "标题") + private String title; + + /** + * 通知类型 + */ + @Schema(description = "通知类型") + private Integer category; + + /** + * 发布日期 + */ + @Schema(description = "发布日期") + private Date releaseTime; + + /** + * 内容 + */ + @Schema(description = "内容") + private String content; + + +} diff --git a/blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/vo/NoticeVO.java b/blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/vo/NoticeVO.java new file mode 100644 index 0000000..3521950 --- /dev/null +++ b/blade-service-api/blade-demo-api/src/main/java/com/example/demo/pojo/vo/NoticeVO.java @@ -0,0 +1,20 @@ +package com.example.demo.pojo.vo; + +import com.example.demo.pojo.entity.Notice; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 通知公告视图类 + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class NoticeVO extends Notice { + + @Schema(description = "通知类型名") + private String categoryName; + +} diff --git a/blade-service-api/blade-zhaocai-api/pom.xml b/blade-service-api/blade-zhaocai-api/pom.xml new file mode 100644 index 0000000..62c67c2 --- /dev/null +++ b/blade-service-api/blade-zhaocai-api/pom.xml @@ -0,0 +1,16 @@ + + + + blade-service-api + org.springblade + ${revision} + + 4.0.0 + + blade-zhaocai-api + ${project.artifactId} + jar + + diff --git a/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlan.java b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlan.java new file mode 100644 index 0000000..7731046 --- /dev/null +++ b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlan.java @@ -0,0 +1,117 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.pojo.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.mp.base.BaseEntity; + +import java.io.Serial; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 招采计划实体类 + * + * @author Chill + */ +@Data +@TableName("blade_procurement_plan") +@EqualsAndHashCode(callSuper = true) +public class ProcurementPlan extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 申请人 + */ + @Schema(description = "申请人") + private String applicant; + + /** + * 申请时间 + */ + @Schema(description = "申请时间") + private LocalDateTime applyTime; + + /** + * 联系电话 + */ + @Schema(description = "联系电话") + private String phone; + + /** + * 所属公司 + */ + @Schema(description = "所属公司") + private String company; + + /** + * 单据号 + */ + @Schema(description = "单据号") + private String billNo; + + /** + * 年度 + */ + @Schema(description = "年度") + private String year; + + /** + * 所属部门 + */ + @Schema(description = "所属部门") + private String dept; + + /** + * 申请信息中的所属公司 + */ + @Schema(description = "申请所属公司") + private String applyCompany; + + /** + * 预估总金额(万元) + */ + @Schema(description = "预估总金额(万元)") + private BigDecimal estimatedAmount; + + /** + * 预估总金额大写 + */ + @Schema(description = "预估总金额大写") + private String amountUpper; + + /** + * 备注 + */ + @Schema(description = "备注") + private String remark; + +} diff --git a/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlanDetail.java b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlanDetail.java new file mode 100644 index 0000000..1486d89 --- /dev/null +++ b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/entity/ProcurementPlanDetail.java @@ -0,0 +1,125 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.pojo.entity; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.core.mp.base.BaseEntity; + +import java.io.Serial; +import java.time.LocalDate; + +/** + * 招采计划明细实体类 + * + * @author Chill + */ +@Data +@TableName("blade_procurement_plan_detail") +@EqualsAndHashCode(callSuper = true) +public class ProcurementPlanDetail extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主表ID + */ + @JsonSerialize(using = ToStringSerializer.class) + @Schema(description = "主表ID") + private Long planId; + + /** + * 年度 + */ + @Schema(description = "年度") + private String year; + + /** + * 计划 + */ + @Schema(description = "计划") + private String plan; + + /** + * 计划类型 + */ + @Schema(description = "计划类型") + private String planType; + + /** + * 序号 + */ + @Schema(description = "序号") + private Integer sortNo; + + /** + * 项目名称 + */ + @Schema(description = "项目名称") + private String projectName; + + /** + * 项目类型 + */ + @Schema(description = "项目类型") + private String projectType; + + /** + * 项目对接人 + */ + @Schema(description = "项目对接人") + private String contactPerson; + + /** + * 计划申请日期 + */ + @Schema(description = "计划申请日期") + private LocalDate applyDate; + + /** + * 期望使用/完成日期 + */ + @Schema(description = "期望使用/完成日期") + private LocalDate expectDate; + + /** + * 项目属性 + */ + @Schema(description = "项目属性") + private String projectAttr; + + /** + * 采购物料明细 + */ + @Schema(description = "采购物料明细") + private String materialDetail; + +} diff --git a/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanDetailVO.java b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanDetailVO.java new file mode 100644 index 0000000..dcc8070 --- /dev/null +++ b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanDetailVO.java @@ -0,0 +1,47 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.pojo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.zhaocai.pojo.entity.ProcurementPlanDetail; + +import java.io.Serial; + +/** + * 招采计划明细视图对象 + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ProcurementPlanDetailVO extends ProcurementPlanDetail { + + @Serial + private static final long serialVersionUID = 1L; + +} diff --git a/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanVO.java b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanVO.java new file mode 100644 index 0000000..a0277e2 --- /dev/null +++ b/blade-service-api/blade-zhaocai-api/src/main/java/org/springblade/zhaocai/pojo/vo/ProcurementPlanVO.java @@ -0,0 +1,54 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.pojo.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.springblade.zhaocai.pojo.entity.ProcurementPlan; + +import java.io.Serial; +import java.util.List; + +/** + * 招采计划视图对象 + * + * @author Chill + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ProcurementPlanVO extends ProcurementPlan { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 项目明细列表 + */ + @Schema(description = "项目明细列表") + private List detailList; + +} diff --git a/blade-service-api/pom.xml b/blade-service-api/pom.xml new file mode 100644 index 0000000..58a10f6 --- /dev/null +++ b/blade-service-api/pom.xml @@ -0,0 +1,70 @@ + + + + BladeX-Biz + org.springblade + ${revision} + + + 4.0.0 + + blade-service-api + ${project.artifactId} + pom + BladeX 微服务API集合 + + + blade-demo-api + blade-zhaocai-api + + + + + org.springblade + blade-starter-mybatis + + + org.springblade + blade-starter-tenant + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + io.springfox + springfox-swagger2 + + + io.swagger + swagger-models + + + + + io.swagger + swagger-models + + + org.springblade + blade-core-auto + provided + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + ${project.name} + + + + + + diff --git a/blade-service/blade-demo/Dockerfile b/blade-service/blade-demo/Dockerfile new file mode 100644 index 0000000..5db262c --- /dev/null +++ b/blade-service/blade-demo/Dockerfile @@ -0,0 +1,13 @@ +FROM bladex/alpine-java:openjdk17_cn_slim + +LABEL maintainer="bladejava@qq.com" + +RUN mkdir -p /blade/desk + +WORKDIR /blade/desk + +EXPOSE 8105 + +COPY ./target/blade-demo.jar ./app.jar + +ENTRYPOINT ["java", "--add-opens", "java.base/java.lang=ALL-UNNAMED", "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"] diff --git a/blade-service/blade-demo/doc/nacos/blade-demo-dev.yaml b/blade-service/blade-demo/doc/nacos/blade-demo-dev.yaml new file mode 100644 index 0000000..fee2be5 --- /dev/null +++ b/blade-service/blade-demo/doc/nacos/blade-demo-dev.yaml @@ -0,0 +1,19 @@ +#自定义配置 +demo: + name: demo-name + +#放行配置 +blade: + secure: + skip-url: + - /demo/** + datasource: + demo: + master: + url: jdbc:mysql://localhost:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8 + username: root + password: root + slave: + url: jdbc:mysql://localhost:3306/bladex_slave?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8 + username: root + password: root diff --git a/blade-service/blade-demo/pom.xml b/blade-service/blade-demo/pom.xml new file mode 100644 index 0000000..6af8da0 --- /dev/null +++ b/blade-service/blade-demo/pom.xml @@ -0,0 +1,87 @@ + + + + + org.springblade + blade-service + ${revision} + + + 4.0.0 + + blade-demo + ${project.artifactId} + jar + + + + + org.springblade + blade-core-boot + + + + org.springblade + blade-starter-db-dynamic + + + + org.springblade + blade-starter-swagger + + + + org.springblade + blade-starter-oss + + + io.minio + minio + + + + org.springblade + blade-starter-liteflow + + + org.springblade + blade-starter-literule + + + + org.springblade + blade-demo-api + + + + org.springblade + blade-core-test + test + + + + org.springblade + blade-core-auto + provided + + + + + + + io.fabric8 + docker-maven-plugin + + ${docker.fabric.skip} + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/DemoApplication.java b/blade-service/blade-demo/src/main/java/com/example/demo/DemoApplication.java new file mode 100644 index 0000000..504d3d4 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/DemoApplication.java @@ -0,0 +1,45 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo; + +import org.springblade.core.cloud.client.BladeCloudApplication; +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.launch.constant.AppConstant; + +/** + * Demo启动器 + * + * @author Chill + */ +@BladeCloudApplication +public class DemoApplication { + + public static void main(String[] args) { + BladeApplication.run(AppConstant.APPLICATION_DEMO_NAME, DemoApplication.class, args); + } + +} + diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/config/DemoConfiguration.java b/blade-service/blade-demo/src/main/java/com/example/demo/config/DemoConfiguration.java new file mode 100644 index 0000000..5e64d3f --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/config/DemoConfiguration.java @@ -0,0 +1,48 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.config; + + +import com.example.demo.props.DemoProperties; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * 配置feign、mybatis包名、properties + * + * @author Chill + */ +@Configuration(proxyBeanMethods = false) +@ComponentScan({"org.springblade", "com.example"}) +@EnableFeignClients({"org.springblade", "com.example"}) +@MapperScan({"org.springblade.**.mapper.**", "com.example.**.mapper.**"}) +@EnableConfigurationProperties(DemoProperties.class) +public class DemoConfiguration { + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/controller/DemoController.java b/blade-service/blade-demo/src/main/java/com/example/demo/controller/DemoController.java new file mode 100644 index 0000000..20a7ff0 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/controller/DemoController.java @@ -0,0 +1,68 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.controller; + +import com.example.demo.props.DemoProperties; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Demo控制器 + * + * @author Chill + */ +@RefreshScope +@RestController +@RequestMapping("demo") +@RequiredArgsConstructor +@Tag(name = "配置接口", description = "即时刷新配置") +public class DemoController { + + /** + * 需要导入blade-demo-dev.yaml文件至nacos + */ + @Value("${demo.name:1}") + private String name; + + private final DemoProperties properties; + + + @GetMapping("name") + public String getName() { + return name; + } + + @GetMapping("name-by-props") + public String getNameByProps() { + return properties.getName(); + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/controller/DynamicController.java b/blade-service/blade-demo/src/main/java/com/example/demo/controller/DynamicController.java new file mode 100644 index 0000000..9dd90d6 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/controller/DynamicController.java @@ -0,0 +1,76 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.controller; + +import com.example.demo.pojo.entity.Notice; +import com.example.demo.service.IDynamicService; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.core.tool.api.R; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 多数据源 + * + * @author Chill + */ +@RestController +@AllArgsConstructor +@RequestMapping("dynamic") +@Tag(name = "多数据源接口", description = "多数据源") +public class DynamicController { + + private final IDynamicService dynamicService; + + /** + * master列表 + */ + @GetMapping("/master-list") + @ApiOperationSupport(order = 1) + @Operation(summary = "master列表", description = "master列表") + public R> masterList() { + List list = dynamicService.masterList(); + return R.data(list); + } + + /** + * slave列表 + */ + @GetMapping("/slave-list") + @ApiOperationSupport(order = 1) + @Operation(summary = "slave列表", description = "slave列表") + public R> slaveList() { + List list = dynamicService.slaveList(); + return R.data(list); + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteFlowController.java b/blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteFlowController.java new file mode 100644 index 0000000..350fdfb --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteFlowController.java @@ -0,0 +1,47 @@ +package com.example.demo.controller; + +import com.example.demo.rule.liteflow.context.BizContext; +import com.yomahub.liteflow.core.FlowExecutor; +import com.yomahub.liteflow.flow.LiteflowResponse; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.tool.jackson.JsonUtil; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * LiteFlowController + * + * @author BladeX + */ +@Slf4j +@RestController +@AllArgsConstructor +@RequestMapping("liteflow") +@Tag(name = "LiteFlow接口", description = "LiteFlow") +public class LiteFlowController { + @Resource + private FlowExecutor flowExecutor; + + @GetMapping("/test") + public String test() { + // 构建上下文 + BizContext bizContext = new BizContext(); + bizContext.setId(1L); + bizContext.setName("测试名称"); + bizContext.setCategory(1); + bizContext.setIsPublish(Boolean.TRUE); + // 启动 demoChain 的规则引擎 + LiteflowResponse resp = flowExecutor.execute2Resp("demoChain", null, bizContext); + if (resp.isSuccess()) { + BizContext contextBean = resp.getContextBean(BizContext.class); + return JsonUtil.toJson(contextBean); + } else { + return "fail"; + } + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteRuleController.java b/blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteRuleController.java new file mode 100644 index 0000000..8bbf9c4 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/controller/LiteRuleController.java @@ -0,0 +1,156 @@ +package com.example.demo.controller; + +import com.example.demo.rule.literule.context.Address; +import com.example.demo.rule.literule.context.OrderContext; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.engine.RuleEngineExecutor; +import org.springblade.core.literule.provider.LiteRuleResponse; +import org.springblade.core.literule.provider.RuleConfig; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.util.concurrent.Executors; + +/** + * LiteRuleController + * + * @author BladeX + */ +@Slf4j +@RestController +@AllArgsConstructor +@RequestMapping("literule") +@Tag(name = "LiteRule接口", description = "LiteRule") +public class LiteRuleController { + @Resource + private RuleEngineExecutor ruleExecutor; + + @GetMapping("/test") + public String test() { + // 测试成功场景 + testSuccessCase(ruleExecutor); + + // 测试失败场景 + testFailureCase(ruleExecutor); + + // 测试异步执行 + testAsyncExecution(ruleExecutor); + + return "执行完毕请查看控制台输出"; + } + + /** + * 测试成功场景 + */ + private static void testSuccessCase(RuleEngineExecutor ruleExecutor) { + log.info("=== [1] 开始测试:正常场景 ==="); + + // 构建上下文 + OrderContext context = OrderContext.builder() + .orderId("ORD-001") + .amount(new BigDecimal("1200")) + .paymentType("ALIPAY") + .shippingAddress(Address.builder() + .country("China") + .province("Guangdong") + .city("Shenzhen") + .detail("Nanshan District") + .build()) + .build(); + + // 构建配置 + RuleConfig config = RuleConfig.builder() + .enableTimeMonitor(true) + .printExecutionTime(true) + .enableLogging(true) + .build(); + + // 执行规则链 + LiteRuleResponse response = ruleExecutor.execute( + "orderChain", + context, + config + ); + + // 处理响应 + log.info("[1.1] 规则链执行结果:{}", response.isSuccess() ? "成功" : "失败"); + log.info("[1.2] 规则链执行耗时:{}毫秒", response.getExecutionTime()); + log.info("[1.3] 订单状态:{}", response.getContext().getStatus()); + log.info("[1.4] 最终金额:{}", response.getContext().getAmount()); + } + + /** + * 测试失败场景 + */ + private static void testFailureCase(RuleEngineExecutor ruleExecutor) { + log.info("=== [2] 开始测试:失败场景 ==="); + + // 构建上下文(缺少必要字段) + OrderContext context = OrderContext.builder() + .orderId("") // 空订单ID + .amount(new BigDecimal("-100")) // 负数金额 + .paymentType("UNKNOWN") // 未知支付方式 + .shippingAddress(Address.builder() + .country("USA") + .province("California") + .city("San Francisco") + .detail("Silicon Valley") + .build()) + .build(); + + // 执行规则链 + LiteRuleResponse response = ruleExecutor.execute( + "orderChain", + context + ); + + // 处理响应 + log.info("[2.1] 规则链执行结果:{}", response.isSuccess() ? "成功" : "失败"); + log.info("[2.2] 错误信息:{}", response.getContext().getErrorMessages()); + } + + /** + * 测试异步执行 + */ + private static void testAsyncExecution(RuleEngineExecutor ruleExecutor) { + log.info("=== [3] 开始测试:异步执行 ==="); + + // 构建上下文 + OrderContext context = OrderContext.builder() + .orderId("ORD-002") + .amount(new BigDecimal("800")) + .paymentType("WECHAT") + .shippingAddress(Address.builder() + .country("USA") + .province("California") + .city("San Francisco") + .detail("Silicon Valley") + .build()) + .build(); + + // 异步执行规则链 + ruleExecutor.executeAsync( + "orderChain", + context, + Executors.newSingleThreadExecutor() + ).thenAccept(response -> { + log.info("[3.1] 异步规则链执行完成"); + log.info("[3.2] 规则链执行结果:{}", response.isSuccess() ? "成功" : "失败"); + log.info("[3.3] 规则链执行耗时:{}毫秒", response.getExecutionTime()); + log.info("[3.4] 订单状态:{}", response.getContext().getStatus()); + log.info("[3.5] 最终金额:{}", response.getContext().getAmount()); + }); + + // 等待异步执行完成 + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/controller/NoticeController.java b/blade-service/blade-demo/src/main/java/com/example/demo/controller/NoticeController.java new file mode 100644 index 0000000..e56f04f --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/controller/NoticeController.java @@ -0,0 +1,130 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.demo.pojo.entity.Notice; +import com.example.demo.service.INoticeService; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.annotations.ApiParam; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.common.cache.CacheNames; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.mp.support.Condition; +import org.springblade.core.mp.support.Query; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.Func; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 控制器 + * + * @author Chill + */ +@RestController +@RequestMapping("notice") +@AllArgsConstructor +@Tag(name = "用户博客", description = "博客接口") +public class NoticeController extends BladeController implements CacheNames { + + private final INoticeService noticeService; + + /** + * 详情 + */ + @GetMapping("/detail") + @ApiOperationSupport(order = 1) + @Operation(summary = "详情", description = "传入notice") + public R detail(Notice notice) { + Notice detail = noticeService.getOne(Condition.getQueryWrapper(notice)); + return R.data(detail); + } + + /** + * 分页 + */ + @GetMapping("/list") + @Parameters({ + @Parameter(name = "category", description = "公告类型", in = ParameterIn.QUERY, schema = @Schema(type = "integer")), + @Parameter(name = "title", description = "公告标题", in = ParameterIn.QUERY, schema = @Schema(type = "string")) + }) + @ApiOperationSupport(order = 2) + @Operation(summary = "分页", description = "传入notice") + public R> list(@Parameter(hidden = true) @RequestParam Map notice, Query query) { + IPage pages = noticeService.page(Condition.getPage(query), Condition.getQueryWrapper(notice, Notice.class)); + return R.data(pages); + } + + /** + * 新增 + */ + @PostMapping("/save") + @ApiOperationSupport(order = 3) + @Operation(summary = "新增", description = "传入notice") + public R save(@RequestBody Notice notice) { + return R.status(noticeService.save(notice)); + } + + /** + * 修改 + */ + @PostMapping("/update") + @ApiOperationSupport(order = 4) + @Operation(summary = "修改", description = "传入notice") + public R update(@RequestBody Notice notice) { + return R.status(noticeService.updateById(notice)); + } + + /** + * 新增或修改 + */ + @PostMapping("/submit") + @ApiOperationSupport(order = 5) + @Operation(summary = "新增或修改", description = "传入notice") + public R submit(@RequestBody Notice notice) { + return R.status(noticeService.saveOrUpdate(notice)); + } + + /** + * 删除 + */ + @PostMapping("/remove") + @ApiOperationSupport(order = 6) + @Operation(summary = "逻辑删除", description = "传入notice") + public R remove(@ApiParam(value = "主键集合") @RequestParam String ids) { + boolean temp = noticeService.deleteLogic(Func.toLongList(ids)); + return R.status(temp); + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/controller/UploadController.java b/blade-service/blade-demo/src/main/java/com/example/demo/controller/UploadController.java new file mode 100644 index 0000000..4b33a02 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/controller/UploadController.java @@ -0,0 +1,57 @@ +package com.example.demo.controller; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import lombok.SneakyThrows; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.boot.file.LocalFile; +import org.springblade.core.oss.MinioTemplate; +import org.springblade.core.oss.model.BladeFile; +import org.springblade.core.tool.api.R; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * UploadController + * + * @author Chill + */ +@RestController +@AllArgsConstructor +@RequestMapping("/notice/upload") +@Tag(name = "对象存储接口", description = "oss上传测试") +public class UploadController extends BladeController { + + private final MinioTemplate minioTemplate; + + /** + * minio上传demo + * + * @param file 上传文件 + * @return BladeFile + */ + @SneakyThrows + @PostMapping("put-object") + public R putMinioObject(@RequestParam MultipartFile file) { + BladeFile bladeFile = minioTemplate.putFile(file); + return R.data(bladeFile); + } + + /** + * 上传本地文件 + * + * @param file 上传文件 + * @return LocalFile + */ + @SneakyThrows + @PostMapping("put-local-object") + public R putLocalObject(@RequestParam MultipartFile file) { + LocalFile localFile = getFile(file); + localFile.transfer(); + return R.data(localFile); + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/feign/NoticeClient.java b/blade-service/blade-demo/src/main/java/com/example/demo/feign/NoticeClient.java new file mode 100644 index 0000000..c334081 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/feign/NoticeClient.java @@ -0,0 +1,56 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.feign; + +import com.example.demo.pojo.entity.Notice; +import com.example.demo.mapper.NoticeMapper; +import io.swagger.v3.oas.annotations.Hidden; +import lombok.AllArgsConstructor; +import org.springblade.core.tool.api.R; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Notice Feign + * + * @author Chill + */ +@Hidden +@RestController +@AllArgsConstructor +public class NoticeClient implements INoticeClient { + + private final NoticeMapper mapper; + + @Override + @GetMapping(TOP) + public R> top(Integer number) { + return R.data(mapper.topList(number)); + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/launcher/DemoLauncherServiceImpl.java b/blade-service/blade-demo/src/main/java/com/example/demo/launcher/DemoLauncherServiceImpl.java new file mode 100644 index 0000000..e7015fc --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/launcher/DemoLauncherServiceImpl.java @@ -0,0 +1,69 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.launcher; + +import org.springblade.core.auto.service.AutoService; +import org.springblade.core.launch.constant.NacosConstant; +import org.springblade.core.launch.service.LauncherService; +import org.springframework.boot.builder.SpringApplicationBuilder; + +import java.util.Properties; + +/** + * 启动参数拓展 + * + * @author Chill + */ +@AutoService(LauncherService.class) +public class DemoLauncherServiceImpl implements LauncherService { + + @Override + public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) { + Properties props = System.getProperties(); + // 指定注册配置信息 + props.setProperty("spring.config.import", String.join(",", + NacosConstant.dataId(), + NacosConstant.dataId(profile), + NacosConstant.dataId(appName, profile), + NacosConstant.dataId("blade-example", profile)) + ); + // 指定注册IP + // PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.ip", "127.0.0.1"); + // 指定注册端口 + // PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.port", "8200"); + // 自定义命名空间 + // PropsUtil.setProperty(props, "spring.cloud.nacos.config.namespace", LauncherConstant.NACOS_NAMESPACE); + // PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.namespace", LauncherConstant.NACOS_NAMESPACE); + // 自定义分组 + // PropsUtil.setProperty(props, "spring.cloud.nacos.config.group", NacosConstant.NACOS_CONFIG_GROUP); + // PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.group", NacosConstant.NACOS_CONFIG_GROUP); + } + + @Override + public int getOrder() { + return 20; + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.java b/blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.java new file mode 100644 index 0000000..01b1624 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.java @@ -0,0 +1,56 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.demo.pojo.entity.Notice; + +import java.util.List; + +/** + * Mapper 接口 + * + * @author Chill + */ +public interface NoticeMapper extends BaseMapper { + + /** + * 前N条数据 + * @param number + * @return + */ + List topList(Integer number); + + /** + * 自定义分页 + * @param page + * @param notice + * @return + */ + List selectNoticePage(IPage page, Notice notice); + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.xml b/blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.xml new file mode 100644 index 0000000..e42ceb9 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/mapper/NoticeMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + select id, + create_user AS createUser, + create_time AS createTime, + update_user AS updateUser, + update_time AS updateTime, + status, + is_deleted AS isDeleted, + title, content + + + + + + + diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/props/DemoProperties.java b/blade-service/blade-demo/src/main/java/com/example/demo/props/DemoProperties.java new file mode 100644 index 0000000..7c3f700 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/props/DemoProperties.java @@ -0,0 +1,18 @@ +package com.example.demo.props; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * DemoProperties + * + * @author Chill + */ +@Data +@ConfigurationProperties(prefix = "demo") +public class DemoProperties { + /** + * 名称 + */ + private String name; +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/AbizRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/AbizRule.java new file mode 100644 index 0000000..8b77fac --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/AbizRule.java @@ -0,0 +1,48 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.rule.liteflow; + +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import lombok.extern.slf4j.Slf4j; + + +/** + * A业务 + * + * @author Chill + */ +@Slf4j +@LiteflowComponent(id = "abizRule", name = "A业务") +public class AbizRule extends NodeComponent { + @Override + public void process() throws Exception { + + log.info("A业务执行完毕"); + + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/BbizRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/BbizRule.java new file mode 100644 index 0000000..fec7639 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/BbizRule.java @@ -0,0 +1,57 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.rule.liteflow; + +import com.example.demo.rule.liteflow.context.BizContext; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeSwitchComponent; +import lombok.extern.slf4j.Slf4j; + + +/** + * A业务 + * + * @author Chill + */ +@Slf4j +@LiteflowComponent(id = "bbizRule", name = "B业务") +public class BbizRule extends NodeSwitchComponent { + + @Override + public String processSwitch() throws Exception { + // 获取上下文 + BizContext contextBean = this.getContextBean(BizContext.class); + // 获取条件 + Boolean isPublish = contextBean.getIsPublish(); + log.info("B业务执行判断"); + // 进行节点判断 + if (isPublish) { + return "cbizRule"; + } else { + return "dbizRule"; + } + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/CbizRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/CbizRule.java new file mode 100644 index 0000000..035a5b2 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/CbizRule.java @@ -0,0 +1,53 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.rule.liteflow; + +import com.example.demo.rule.liteflow.context.BizContext; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import lombok.extern.slf4j.Slf4j; + + +/** + * A业务 + * + * @author Chill + */ +@Slf4j +@LiteflowComponent(id = "cbizRule", name = "C业务") +public class CbizRule extends NodeComponent { + @Override + public void process() throws Exception { + // 获取上下文 + BizContext contextBean = this.getContextBean(BizContext.class); + // 修改上下文 + contextBean.setName(contextBean.getName() + "-> C"); + + log.info("C业务执行完毕"); + + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/DbizRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/DbizRule.java new file mode 100644 index 0000000..7827fdd --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/DbizRule.java @@ -0,0 +1,54 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.rule.liteflow; + +import com.example.demo.rule.liteflow.context.BizContext; +import com.yomahub.liteflow.annotation.LiteflowComponent; +import com.yomahub.liteflow.core.NodeComponent; +import lombok.extern.slf4j.Slf4j; + + +/** + * A业务 + * + * @author Chill + */ +@Slf4j +@LiteflowComponent(id = "dbizRule", name = "D业务") +public class DbizRule extends NodeComponent { + @Override + public void process() throws Exception { + // 获取上下文 + BizContext contextBean = this.getContextBean(BizContext.class); + // 修改上下文 + contextBean.setName(contextBean.getName() + "-> D"); + + log.info("D业务执行完毕"); + + + } + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/context/BizContext.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/context/BizContext.java new file mode 100644 index 0000000..5c2f627 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/liteflow/context/BizContext.java @@ -0,0 +1,21 @@ +package com.example.demo.rule.liteflow.context; + +import lombok.Data; + +/** + * 上下文类 + * + * @author Chill + */ +@Data +public class BizContext { + + private Long id; + + private String name; + + private Integer category; + + private Boolean isPublish; + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/AlipayRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/AlipayRule.java new file mode 100644 index 0000000..95eec7c --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/AlipayRule.java @@ -0,0 +1,26 @@ +package com.example.demo.rule.literule; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.annotation.LiteRuleComponent; +import org.springblade.core.literule.core.RuleComponent; +import com.example.demo.rule.literule.context.OrderContext; + +/** + * AlipayRule + * + * @author BladeX + */ +@Slf4j +@LiteRuleComponent(id = "alipayRule") +public class AlipayRule extends RuleComponent { + + @Override + protected void process() { + OrderContext context = getContextBean(OrderContext.class); + log.info("[规则4-支付宝] 处理支付宝支付:订单号={}, 金额={}", + context.getOrderId(), context.getAmount()); + + // 模拟支付处理 + context.setStatus("PAID"); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/BankPayRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/BankPayRule.java new file mode 100644 index 0000000..63341ee --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/BankPayRule.java @@ -0,0 +1,26 @@ +package com.example.demo.rule.literule; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.annotation.LiteRuleComponent; +import org.springblade.core.literule.core.RuleComponent; +import com.example.demo.rule.literule.context.OrderContext; + +/** + * BankPayRule + * + * @author BladeX + */ +@Slf4j +@LiteRuleComponent(id = "bankPayRule") +public class BankPayRule extends RuleComponent { + + @Override + protected void process() { + OrderContext context = getContextBean(OrderContext.class); + log.info("[规则4-银行] 处理银行卡支付:订单号={}, 金额={}", + context.getOrderId(), context.getAmount()); + + // 模拟支付处理 + context.setStatus("PAID"); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderAmountRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderAmountRule.java new file mode 100644 index 0000000..2f161f2 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderAmountRule.java @@ -0,0 +1,53 @@ +package com.example.demo.rule.literule; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.annotation.LiteRuleComponent; +import org.springblade.core.literule.core.RuleComponent; +import com.example.demo.rule.literule.context.Address; +import com.example.demo.rule.literule.context.OrderContext; + +import java.math.BigDecimal; + +/** + * OrderAmountRule + * + * @author BladeX + */ +@Slf4j +@LiteRuleComponent(id = "orderAmountRule") +public class OrderAmountRule extends RuleComponent { + + @Override + protected void process() { + OrderContext context = getContextBean(OrderContext.class); + BigDecimal amount = context.getAmount(); + + // 计算折扣 + BigDecimal discount = calculateDiscount(amount); + + // 计算运费 + BigDecimal shipping = calculateShipping(context.getShippingAddress()); + + // 更新订单金额 + BigDecimal finalAmount = amount.subtract(discount).add(shipping); + context.setAmount(finalAmount); + + log.info("[规则2] 订单金额计算完成:{} = {} - {} + {}", finalAmount, amount, discount, shipping); + } + + private BigDecimal calculateDiscount(BigDecimal amount) { + // 折扣计算逻辑:金额大于1000打9折 + if (amount.compareTo(new BigDecimal("1000")) > 0) { + return amount.multiply(new BigDecimal("0.1")); + } + return BigDecimal.ZERO; + } + + private BigDecimal calculateShipping(Address address) { + // 运费计算逻辑:根据地址计算 + if ("China".equals(address.getCountry())) { + return new BigDecimal("10"); + } + return new BigDecimal("50"); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderValidateRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderValidateRule.java new file mode 100644 index 0000000..a549741 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/OrderValidateRule.java @@ -0,0 +1,52 @@ +package com.example.demo.rule.literule; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.annotation.LiteRuleComponent; +import org.springblade.core.literule.core.RuleComponent; +import com.example.demo.rule.literule.context.Address; +import com.example.demo.rule.literule.context.OrderContext; + +import java.math.BigDecimal; + +/** + * OrderValidateRule + * + * @author BladeX + */ +@Slf4j +@LiteRuleComponent(id = "orderValidateRule") +public class OrderValidateRule extends RuleComponent { + + @Override + protected void process() { + OrderContext context = getContextBean(OrderContext.class); + + // 校验订单ID + if (isEmpty(context.getOrderId())) { + context.addError("订单ID不能为空"); + log.info("[规则1] 订单校验不通过:{}", context.getErrorMessages()); + return; + } + + // 校验金额 + if (context.getAmount() == null || context.getAmount().compareTo(BigDecimal.ZERO) <= 0) { + context.addError("订单金额无效"); + log.info("[规则1] 订单校验不通过:{}", context.getErrorMessages()); + return; + } + + // 校验地址 + Address address = context.getShippingAddress(); + if (address == null) { + context.addError("收货地址不能为空"); + log.info("[规则1] 订单校验不通过:{}", context.getErrorMessages()); + return; + } + + log.info("[规则1] 订单校验通过:{}", context.getOrderId()); + } + + private boolean isEmpty(String str) { + return str == null || str.trim().isEmpty(); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/PaymentSwitchRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/PaymentSwitchRule.java new file mode 100644 index 0000000..b518eab --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/PaymentSwitchRule.java @@ -0,0 +1,45 @@ +package com.example.demo.rule.literule; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.annotation.LiteRuleComponent; +import org.springblade.core.literule.core.RuleSwitchComponent; +import com.example.demo.rule.literule.context.OrderContext; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * PaymentRouteRule + * + * @author BladeX + */ +@Slf4j +@LiteRuleComponent(id = "paymentSwitchRule") +public class PaymentSwitchRule extends RuleSwitchComponent { + + private static final Map PAYMENT_RULE_MAP = new HashMap<>(); + + static { + PAYMENT_RULE_MAP.put("ALIPAY", "alipayRule"); + PAYMENT_RULE_MAP.put("WECHAT", "wechatPayRule"); + PAYMENT_RULE_MAP.put("BANK", "bankPayRule"); + } + + @Override + protected List process() { + OrderContext context = getContextBean(OrderContext.class); + String paymentType = context.getPaymentType(); + + log.info("[规则3] 支付路由选择:{}", paymentType); + + String payName = PAYMENT_RULE_MAP.get(paymentType); + if (payName != null) { + return Collections.singletonList(payName); + } else { + context.addError("不支持的支付方式:" + paymentType); + return Collections.emptyList(); + } + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/WechatPayRule.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/WechatPayRule.java new file mode 100644 index 0000000..2079209 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/WechatPayRule.java @@ -0,0 +1,26 @@ +package com.example.demo.rule.literule; + +import lombok.extern.slf4j.Slf4j; +import org.springblade.core.literule.annotation.LiteRuleComponent; +import org.springblade.core.literule.core.RuleComponent; +import com.example.demo.rule.literule.context.OrderContext; + +/** + * WechatPayRule + * + * @author BladeX + */ +@Slf4j +@LiteRuleComponent(id = "wechatPayRule") +public class WechatPayRule extends RuleComponent { + + @Override + protected void process() { + OrderContext context = getContextBean(OrderContext.class); + log.info("[规则4-微信] 处理微信支付:订单号={}, 金额={}", + context.getOrderId(), context.getAmount()); + + // 模拟支付处理 + context.setStatus("PAID"); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/builder/OrderRuleBuilder.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/builder/OrderRuleBuilder.java new file mode 100644 index 0000000..14ed22e --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/builder/OrderRuleBuilder.java @@ -0,0 +1,25 @@ +package com.example.demo.rule.literule.builder; + +import org.springblade.core.literule.annotation.RuleEngineComponent; +import org.springblade.core.literule.builder.LiteRule; +import org.springblade.core.literule.builder.RuleBuilder; +import org.springblade.core.literule.builder.chain.RuleChain; + +/** + * 订单处理规则链构建器 + */ +@RuleEngineComponent(id = "orderChain") +public class OrderRuleBuilder implements RuleBuilder { + @Override + public RuleChain build() { + // 创建支付处理分支规则 + RuleChain paymentRule = LiteRule.SWITCH("paymentSwitchRule") + .TO("alipayRule", "wechatPayRule", "bankPayRule") + .build(); + + // 创建完整规则链 + return LiteRule.THEN("orderValidateRule", "orderAmountRule") + .THEN(paymentRule) + .build(); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/Address.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/Address.java new file mode 100644 index 0000000..a832b60 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/Address.java @@ -0,0 +1,22 @@ +package com.example.demo.rule.literule.context; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 地址实体 + * + * @author BladeX + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Address { + private String country; + private String province; + private String city; + private String detail; +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/OrderContext.java b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/OrderContext.java new file mode 100644 index 0000000..77b706c --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/rule/literule/context/OrderContext.java @@ -0,0 +1,24 @@ +package com.example.demo.rule.literule.context; + +import lombok.*; +import org.springblade.core.literule.core.RuleContextComponent; + +import java.math.BigDecimal; + +/** + * 订单上下文 + * + * @author BladeX + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class OrderContext extends RuleContextComponent { + private String orderId; + private BigDecimal amount; + private String paymentType; + private String status; + private Address shippingAddress; +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/service/IDynamicService.java b/blade-service/blade-demo/src/main/java/com/example/demo/service/IDynamicService.java new file mode 100644 index 0000000..2ec711e --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/service/IDynamicService.java @@ -0,0 +1,54 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.service; + +import com.example.demo.pojo.entity.Notice; +import org.springblade.core.mp.base.BaseService; + +import java.util.List; + +/** + * 服务类 + * + * @author Chill + */ +public interface IDynamicService extends BaseService { + + /** + * master数据源的列表 + * + * @return + */ + List masterList(); + + /** + * slave数据源的列表 + * + * @return + */ + List slaveList(); + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/service/INoticeService.java b/blade-service/blade-demo/src/main/java/com/example/demo/service/INoticeService.java new file mode 100644 index 0000000..9bd5433 --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/service/INoticeService.java @@ -0,0 +1,47 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.core.mp.base.BaseService; +import com.example.demo.pojo.entity.Notice; + +/** + * 服务类 + * + * @author Chill + */ +public interface INoticeService extends BaseService { + + /** + * 自定义分页 + * @param page + * @param notice + * @return + */ + IPage selectNoticePage(IPage page, Notice notice); + +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/service/impl/DynamicServiceImpl.java b/blade-service/blade-demo/src/main/java/com/example/demo/service/impl/DynamicServiceImpl.java new file mode 100644 index 0000000..9520d2d --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/service/impl/DynamicServiceImpl.java @@ -0,0 +1,30 @@ +package com.example.demo.service.impl; + +import com.baomidou.dynamic.datasource.annotation.DS; +import com.example.demo.pojo.entity.Notice; +import com.example.demo.mapper.NoticeMapper; +import com.example.demo.service.IDynamicService; +import org.springblade.core.mp.base.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * DynamicServiceImpl + * + * @author Chill + */ +@Service +public class DynamicServiceImpl extends BaseServiceImpl implements IDynamicService { + + @Override + public List masterList() { + return this.list(); + } + + @Override + @DS("slave") + public List slaveList() { + return this.list(); + } +} diff --git a/blade-service/blade-demo/src/main/java/com/example/demo/service/impl/NoticeServiceImpl.java b/blade-service/blade-demo/src/main/java/com/example/demo/service/impl/NoticeServiceImpl.java new file mode 100644 index 0000000..0ed1fce --- /dev/null +++ b/blade-service/blade-demo/src/main/java/com/example/demo/service/impl/NoticeServiceImpl.java @@ -0,0 +1,48 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package com.example.demo.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.demo.mapper.NoticeMapper; +import com.example.demo.service.INoticeService; +import org.springblade.core.mp.base.BaseServiceImpl; +import com.example.demo.pojo.entity.Notice; +import org.springframework.stereotype.Service; + +/** + * 服务实现类 + * + * @author Chill + */ +@Service +public class NoticeServiceImpl extends BaseServiceImpl implements INoticeService { + + @Override + public IPage selectNoticePage(IPage page, Notice notice) { + return page.setRecords(baseMapper.selectNoticePage(page, notice)); + } + +} diff --git a/blade-service/blade-demo/src/main/resources/application-dev.yml b/blade-service/blade-demo/src/main/resources/application-dev.yml new file mode 100644 index 0000000..4d0284a --- /dev/null +++ b/blade-service/blade-demo/src/main/resources/application-dev.yml @@ -0,0 +1,30 @@ +#服务器端口 +server: + port: 8200 + +#Spring配置 +spring: + datasource: + #主数据源信息 + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${blade.datasource.demo.master.url} + username: ${blade.datasource.demo.master.username} + password: ${blade.datasource.demo.master.password} + #动态数据源信息 + dynamic: + druid: + #通用校验配置 + validation-query: select 1 + #设置默认的数据源或者数据源组,默认值即为master + primary: master + datasource: + slave: + druid: + #独立校验配置 + validation-query: select 1 + #oracle校验 + #validation-query: select 1 from dual + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${blade.datasource.demo.slave.url} + username: ${blade.datasource.demo.slave.username} + password: ${blade.datasource.demo.slave.password} diff --git a/blade-service/blade-demo/src/main/resources/application-prod.yml b/blade-service/blade-demo/src/main/resources/application-prod.yml new file mode 100644 index 0000000..4c6a542 --- /dev/null +++ b/blade-service/blade-demo/src/main/resources/application-prod.yml @@ -0,0 +1,31 @@ +#服务器端口 +server: + port: 8200 + +#数据源配置 +#spring: +# datasource: +# url: ${blade.datasource.prod.url} +# username: ${blade.datasource.prod.username} +# password: ${blade.datasource.prod.password} + +spring: + #排除DruidDataSourceAutoConfigure + autoconfigure: + exclude: com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure + datasource: + dynamic: + druid: + proxy-filters: + - sqlLogInterceptor + #设置默认的数据源或者数据源组,默认值即为master + primary: master + datasource: + master: + url: ${blade.datasource.demo.master.url} + username: ${blade.datasource.demo.master.username} + password: ${blade.datasource.demo.master.password} + slave: + url: ${blade.datasource.demo.slave.url} + username: ${blade.datasource.demo.slave.username} + password: ${blade.datasource.demo.slave.password} diff --git a/blade-service/blade-demo/src/main/resources/application-test.yml b/blade-service/blade-demo/src/main/resources/application-test.yml new file mode 100644 index 0000000..9d5852e --- /dev/null +++ b/blade-service/blade-demo/src/main/resources/application-test.yml @@ -0,0 +1,31 @@ +#服务器端口 +server: + port: 8200 + +#数据源配置 +#spring: +# datasource: +# url: ${blade.datasource.test.url} +# username: ${blade.datasource.test.username} +# password: ${blade.datasource.test.password} + +spring: + #排除DruidDataSourceAutoConfigure + autoconfigure: + exclude: com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure + datasource: + url: ${blade.datasource.test.url} + username: ${blade.datasource.test.username} + password: ${blade.datasource.test.password} + dynamic: + druid: + proxy-filters: + - sqlLogInterceptor + #设置默认的数据源或者数据源组,默认值即为master + primary: master +# datasource: +# master: +# slave: +# url: ${blade.datasource.test.slave.url} +# username: ${blade.datasource.test.slave.username} +# password: ${blade.datasource.test.slave.password} diff --git a/blade-service/blade-demo/src/main/resources/application.yml b/blade-service/blade-demo/src/main/resources/application.yml new file mode 100644 index 0000000..7ca2578 --- /dev/null +++ b/blade-service/blade-demo/src/main/resources/application.yml @@ -0,0 +1,29 @@ +#mybatis-plus配置 +mybatis-plus: + mapper-locations: classpath:com/example/**/mapper/*Mapper.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.example.**.entity + +#swagger扫描路径配置 +swagger: + base-packages: + - org.springblade + - com.example + +#oss配置 +oss: + enabled: true + name: minio + tenant-mode: false + endpoint: http://127.0.0.1:9000 + access-key: D99KGE6ZTQXSATTJWU24 + secret-key: QyVqGnhIQQE734UYSUFlGOZViE6+ZlDEfUG3NjhJ + bucket-name: bladex + +#blade配置 +blade: + #本地文件上传 + file: + remote-mode: true + upload-domain: http://localhost:8999 + remote-path: /usr/share/nginx/html diff --git a/blade-service/blade-demo/src/main/resources/liteflow/demo.el.xml b/blade-service/blade-demo/src/main/resources/liteflow/demo.el.xml new file mode 100644 index 0000000..2330ec6 --- /dev/null +++ b/blade-service/blade-demo/src/main/resources/liteflow/demo.el.xml @@ -0,0 +1,9 @@ + + + + THEN( + abizRule, + SWITCH(bbizRule).TO(cbizRule, dbizRule) + ); + + diff --git a/blade-service/blade-demo/src/test/java/org/springblade/test/BladeDemoTest.java b/blade-service/blade-demo/src/test/java/org/springblade/test/BladeDemoTest.java new file mode 100644 index 0000000..1b6145b --- /dev/null +++ b/blade-service/blade-demo/src/test/java/org/springblade/test/BladeDemoTest.java @@ -0,0 +1,31 @@ +package org.springblade.test; + +import com.example.demo.DemoApplication; +import com.example.demo.service.INoticeService; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springblade.core.test.BladeBootTest; +import org.springblade.core.test.BladeSpringExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * Blade单元测试 + * + * @author Chill + */ +@ExtendWith(BladeSpringExtension.class) +@SpringBootTest(classes = DemoApplication.class) +@BladeBootTest(appName = "blade-demo", profile = "test", enableLoader = true) +public class BladeDemoTest { + + @Autowired + private INoticeService noticeService; + + @Test + public void contextLoads() { + long count = noticeService.count(); + System.out.println("notice数量:[" + count + "] 个"); + } + +} diff --git a/blade-service/blade-demo/src/test/java/org/springblade/test/SensitiveTest.java b/blade-service/blade-demo/src/test/java/org/springblade/test/SensitiveTest.java new file mode 100644 index 0000000..4bd4df7 --- /dev/null +++ b/blade-service/blade-demo/src/test/java/org/springblade/test/SensitiveTest.java @@ -0,0 +1,298 @@ +package org.springblade.test; + +import lombok.Data; +import org.springblade.core.tool.jackson.JsonUtil; +import org.springblade.core.tool.jackson.Sensitive; +import org.springblade.core.tool.sensitive.SensitiveConfig; +import org.springblade.core.tool.sensitive.SensitiveType; +import org.springblade.core.tool.sensitive.SensitiveUtil; +import org.springblade.core.tool.sensitive.SensitiveWord; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * SensitiveUtil使用示例 + * 展示各种使用场景和数据脱敏效果 + * + * @author BladeX + */ +public class SensitiveTest { + + public static void main(String[] args) { + // 演示所有使用场景 + demonstrateBasicUsage(); + demonstrateCustomConfig(); + demonstrateSingleType(); + demonstrateMultipleTypes(); + demonstrateCustomRegex(); + demonstrateSensitiveWords(); + demonstrateComplexExample(); + demonstrateJacksonSerializer(); + } + + /** + * 1. 基础用法演示 - 使用默认配置 + */ + private static void demonstrateBasicUsage() { + System.out.println("\n=== 1.基础用法演示 ==="); + + String content = "客户信息:\n" + + "手机号: 13812345678\n" + + "邮箱: test@example.com\n" + + "身份证: 310123199001011234\n" + + "银行卡: 6222021234567890123"; + + String result = SensitiveUtil.process(content); + System.out.println("原始内容:\n" + content); + System.out.println("\n处理后:\n" + result); + } + + /** + * 2. 自定义配置演示 + */ + private static void demonstrateCustomConfig() { + System.out.println("\n=== 2.自定义配置演示 ==="); + + // 创建自定义配置 + SensitiveConfig config = SensitiveConfig.builder() + .sensitiveTypes(EnumSet.of( + SensitiveType.MOBILE, + SensitiveType.EMAIL + )) + .sensitiveWords(EnumSet.of( + SensitiveWord.SECURE, + SensitiveWord.AUTHENTICATION + )) + .customPatterns(Map.of( + "CustomerId", Pattern.compile("(?<=CustomerID:)\\s*\\w+"), + "ProjectCode", Pattern.compile("(?<=ProjectCode:)\\s*\\w+") + )) + .processLineByLine(true) + .replacement("***已隐藏***") + .build(); + + String content = "用户数据:\n" + + "手机: 13812345678\n" + + "CustomerID: USER123456\n" + + "ProjectCode: PRJ789\n" + + "password=admin123\n" + + "token=abcdef"; + + // 转换为LogVO示例 + LogVO logVO = SensitiveUtil.process(content, config, data -> { + LogVO dto = new LogVO(); + dto.setContent(data); + dto.setTimestamp(System.currentTimeMillis()); + return dto; + }); + + System.out.println("原始内容:\n" + content); + System.out.println("\n处理后:\n" + logVO.getContent()); + } + + /** + * 3. 单个敏感类型处理演示 + */ + private static void demonstrateSingleType() { + System.out.println("\n=== 3.单个敏感类型处理演示 ==="); + + String content = "联系方式:13812345678,邮箱:test@example.com"; + String result = SensitiveUtil.process(content, SensitiveType.MOBILE); + + System.out.println("原始内容:" + content); + System.out.println("处理后:" + result); + } + + /** + * 4. 多个敏感类型处理演示 + */ + private static void demonstrateMultipleTypes() { + System.out.println("\n=== 4.多个敏感类型处理演示 ==="); + + String content = "用户资料:\n" + + "手机:13812345678\n" + + "银行卡:6222021234567890123"; + + UserVO userVO = SensitiveUtil.process(content, EnumSet.of( + SensitiveType.MOBILE, + SensitiveType.BANK_CARD + ), data -> { + UserVO vo = new UserVO(); + vo.setUserInfo(data); + return vo; + }); + + System.out.println("原始内容:\n" + content); + System.out.println("\n处理后:\n" + userVO.getUserInfo()); + } + + /** + * 5. 自定义正则处理演示 + */ + private static void demonstrateCustomRegex() { + System.out.println("\n=== 5.自定义正则处理演示 ==="); + + // 简单版本 + String simpleContent = "订单号:ORDER20250101001"; + String simpleResult = SensitiveUtil.processWithRegex( + simpleContent, + "ORDER\\d{8}\\d{3}" + ); + + System.out.println("简单版本:"); + System.out.println("原始内容:" + simpleContent); + System.out.println("处理后:" + simpleResult); + + // 自定义替换符版本 + String customContent = "项目编号:PRJ20250101"; + String customResult = SensitiveUtil.processWithRegex( + customContent, + "PRJ\\d{8}", + "PRJ********" + ); + + System.out.println("\n自定义替换符版本:"); + System.out.println("原始内容:" + customContent); + System.out.println("处理后:" + customResult); + } + + /** + * 6. 敏感词处理演示 + */ + private static void demonstrateSensitiveWords() { + System.out.println("\n=== 6.敏感词处理演示 ==="); + + String content = "系统配置:\n" + + "password=admin123\n" + + "api_key=sk_test_123456\n" + + "secret=my_secret_key\n" + + "test=my_test_key"; + + List sensitiveWords = Arrays.asList("password", "api_key", "secret"); + + // 完整版本的敏感词处理 + String result = SensitiveUtil.processWithWords( + content, + sensitiveWords, + "***",// 可不填 + true // 可不填 + ); + + System.out.println("原始内容:\n" + content); + System.out.println("\n处理后:\n" + result); + } + + /** + * 7. 复杂场景示例 + */ + private static void demonstrateComplexExample() { + System.out.println("\n=== 7.复杂场景示例 ==="); + + String content = "API调用日志:\n" + + "时间:2024-01-01 10:30:00\n" + + "请求头:\n" + + "Authorization: Bearer sk_test_123456\n" + + "请求体:\n" + + "{\n" + + " \"user\": {\n" + + " \"mobile\": \"13812345678\",\n" + + " \"email\": \"test@example.com\",\n" + + " \"idCard\": \"310123199001011234\",\n" + + " \"test\": \"2333333333\"\n" + + " },\n" + + " \"payment\": {\n" + + " \"cardNo\": \"6222021234567890123\",\n" + + " \"secretKey\": \"payment_secret_123\"\n" + + " }\n" + + "}"; + + // 创建复杂配置 + SensitiveConfig complexConfig = SensitiveConfig.builder() + .sensitiveTypes(EnumSet.of( + SensitiveType.MOBILE, + SensitiveType.EMAIL, + SensitiveType.ID_CARD + )) + .sensitiveWords(EnumSet.allOf(SensitiveWord.class)) + .customPatterns(Map.of( + "Bearer", Pattern.compile("Bearer\\s+[\\w-]+"), + "SecretKey", Pattern.compile("(?<=secretKey\":\\s*\")[^\"]+") + )) + .processLineByLine(true) + .replacement("***") + .build(); + + ApiLogVO apiLogVO = SensitiveUtil.process(content, complexConfig, data -> { + ApiLogVO vo = new ApiLogVO(); + vo.setRequestLog(data); + vo.setTimestamp(System.currentTimeMillis()); + return vo; + }); + + System.out.println("原始内容:\n" + content); + System.out.println("\n处理后:\n" + apiLogVO.getRequestLog()); + } + + /** + * 8. jackson序列化场景 + */ + private static void demonstrateJacksonSerializer() { + System.out.println("\n=== 8.jackson序列化场景 ==="); + + AccountVO accountVO = new AccountVO(); + accountVO.setMobile("13812345678"); + accountVO.setEmail("test@example.com"); + accountVO.setCredential("系统配置:\n" + + "password=admin123\n" + + "api_key=sk_test_123456\n" + + "secret=my_secret_key\n" + + "test=my_test"); + accountVO.setToken("Bearer password 123456"); + accountVO.setOrderId("订单号:ORDER20250101001"); + + System.out.println("原始内容:\n" + accountVO); + System.out.println("\n处理后:\n" + JsonUtil.toJson(accountVO)); + } + + /** + * 数据传输对象 + */ + @Data + static class LogVO { + private String content; + private long timestamp; + } + + @Data + static class UserVO { + private String userInfo; + } + + @Data + static class ApiLogVO { + private String requestLog; + private long timestamp; + } + + @Data + static class AccountVO { + @Sensitive(type = SensitiveType.MOBILE) + private String mobile; + + @Sensitive(type = SensitiveType.EMAIL) + private String email; + + @Sensitive(word = SensitiveWord.SECURE) + private String credential; + + @Sensitive(words = {"password", "secret"}) + private String token; + + @Sensitive(regex = "ORDER(.{2}).*(.{2})", replacement = "ORDER$1****$2") + private String orderId; + } +} diff --git a/blade-service/blade-demo/src/test/java/org/springblade/test/launcher/DemoTestLauncherServiceImpl.java b/blade-service/blade-demo/src/test/java/org/springblade/test/launcher/DemoTestLauncherServiceImpl.java new file mode 100644 index 0000000..0fa96dd --- /dev/null +++ b/blade-service/blade-demo/src/test/java/org/springblade/test/launcher/DemoTestLauncherServiceImpl.java @@ -0,0 +1,60 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.test.launcher; + +import org.springblade.common.constant.LauncherConstant; +import org.springblade.core.auto.service.AutoService; +import org.springblade.core.launch.constant.NacosConstant; +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 Chill + */ +@AutoService(LauncherService.class) +public class DemoTestLauncherServiceImpl implements LauncherService { + + @Override + public void launcher(SpringApplicationBuilder builder, String appName, String profile, boolean isLocalDev) { + Properties props = System.getProperties(); + PropsUtil.setProperty(props, "spring.cloud.nacos.config.extension-configs[0].data-id", NacosConstant.dataId("example", profile)); + PropsUtil.setProperty(props, "spring.cloud.nacos.config.extension-configs[0].group", NacosConstant.NACOS_CONFIG_GROUP); + PropsUtil.setProperty(props, "spring.cloud.nacos.config.extension-configs[0].refresh", NacosConstant.NACOS_CONFIG_REFRESH); + PropsUtil.setProperty(props, "spring.cloud.nacos.discovery.server-addr", LauncherConstant.NACOS_DEV_ADDR); + PropsUtil.setProperty(props, "spring.cloud.nacos.config.server-addr", LauncherConstant.NACOS_DEV_ADDR); + PropsUtil.setProperty(props, "spring.cloud.sentinel.transport.dashboard", LauncherConstant.SENTINEL_DEV_ADDR); + } + + @Override + public int getOrder() { + return 10; + } +} diff --git a/blade-service/blade-demo/src/test/resources/application-dev.yml b/blade-service/blade-demo/src/test/resources/application-dev.yml new file mode 100644 index 0000000..880f843 --- /dev/null +++ b/blade-service/blade-demo/src/test/resources/application-dev.yml @@ -0,0 +1,41 @@ +#服务器端口 +server: + port: 8200 + +#数据源配置 +#spring: +# datasource: +# url: ${blade.datasource.dev.url} +# username: ${blade.datasource.dev.username} +# password: ${blade.datasource.dev.password} + +spring: + datasource: + dynamic: + druid: + #通用校验配置 + validation-query: select 1 + #启用sql日志拦截器 + proxy-filters: + - sqlLogInterceptor + #设置默认的数据源或者数据源组,默认值即为master + primary: master + datasource: + master: + druid: + #独立校验配置 + validation-query: select 1 + #oracle校验 + #validation-query: select 1 from dual + url: ${blade.datasource.demo.master.url} + username: ${blade.datasource.demo.master.username} + password: ${blade.datasource.demo.master.password} + slave: + druid: + #独立校验配置 + validation-query: select 1 + #oracle校验 + #validation-query: select 1 from dual + url: ${blade.datasource.demo.slave.url} + username: ${blade.datasource.demo.slave.username} + password: ${blade.datasource.demo.slave.password} diff --git a/blade-service/blade-demo/src/test/resources/application-test.yml b/blade-service/blade-demo/src/test/resources/application-test.yml new file mode 100644 index 0000000..2842aa4 --- /dev/null +++ b/blade-service/blade-demo/src/test/resources/application-test.yml @@ -0,0 +1,10 @@ +#服务器端口 +server: + port: 8200 + +#数据源配置 +spring: + datasource: + url: ${blade.datasource.test.url} + username: ${blade.datasource.test.username} + password: ${blade.datasource.test.password} diff --git a/blade-service/blade-demo/src/test/resources/application.yml b/blade-service/blade-demo/src/test/resources/application.yml new file mode 100644 index 0000000..67bcebf --- /dev/null +++ b/blade-service/blade-demo/src/test/resources/application.yml @@ -0,0 +1,21 @@ +#mybatis-plus配置 +mybatis-plus: + mapper-locations: classpath:com/example/**/mapper/*Mapper.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: com.example.**.entity + +#swagger扫描路径配置 +swagger: + base-packages: + - org.springbalde + - com.example + +#oss配置 +oss: + enabled: true + name: minio + tenant-mode: false + endpoint: http://127.0.0.1:9000 + access-key: D99KGE6ZTQXSATTJWU24 + secret-key: QyVqGnhIQQE734UYSUFlGOZViE6+ZlDEfUG3NjhJ + bucket-name: bladex diff --git a/blade-service/blade-zhaocai/pom.xml b/blade-service/blade-zhaocai/pom.xml new file mode 100644 index 0000000..e207177 --- /dev/null +++ b/blade-service/blade-zhaocai/pom.xml @@ -0,0 +1,69 @@ + + + + + org.springblade + blade-service + ${revision} + + + 4.0.0 + + blade-zhaocai + ${project.artifactId} + jar + + + + + org.springblade + blade-core-boot + + + + org.springblade + blade-starter-db-dynamic + + + + org.springblade + blade-starter-swagger + + + + org.springblade + blade-zhaocai-api + + + + org.springblade + blade-core-test + test + + + + org.springblade + blade-core-auto + provided + + + + + + + io.fabric8 + docker-maven-plugin + + ${docker.fabric.skip} + + + + org.apache.maven.plugins + maven-antrun-plugin + + + + + diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/ZhaocaiApplication.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/ZhaocaiApplication.java new file mode 100644 index 0000000..e743bba --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/ZhaocaiApplication.java @@ -0,0 +1,44 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai; + +import org.springblade.core.cloud.client.BladeCloudApplication; +import org.springblade.core.launch.BladeApplication; +import org.springblade.core.launch.constant.AppConstant; + +/** + * 招采服务启动器 + * + * @author Chill + */ +@BladeCloudApplication +public class ZhaocaiApplication { + + public static void main(String[] args) { + BladeApplication.run(AppConstant.APPLICATION_NAME_PREFIX + "zhaocai", ZhaocaiApplication.class, args); + } + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/config/ZhaocaiConfiguration.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/config/ZhaocaiConfiguration.java new file mode 100644 index 0000000..f426ea3 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/config/ZhaocaiConfiguration.java @@ -0,0 +1,62 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.config; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * 配置 feign、mybatis 包名 + * + * @author Chill + */ +@Configuration(proxyBeanMethods = false) +@ComponentScan({"org.springblade"}) +@EnableFeignClients({"org.springblade"}) +@MapperScan({"org.springblade.**.mapper.**"}) +public class ZhaocaiConfiguration { + + @Bean + @Primary + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + template.setKeySerializer(new StringRedisSerializer()); + template.setHashKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); + return template; + } + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/controller/ProcurementPlanController.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/controller/ProcurementPlanController.java new file mode 100644 index 0000000..395b756 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/controller/ProcurementPlanController.java @@ -0,0 +1,130 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springblade.core.boot.ctrl.BladeController; +import org.springblade.core.mp.support.Condition; +import org.springblade.core.mp.support.Query; +import org.springblade.core.tool.api.R; +import org.springblade.core.tool.utils.Func; +import org.springblade.zhaocai.pojo.entity.ProcurementPlan; +import org.springblade.zhaocai.pojo.vo.ProcurementPlanVO; +import org.springblade.zhaocai.service.IProcurementPlanService; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 招采计划控制器 + * + * @author Chill + */ +@RestController +@RequestMapping("procurement-plan") +@AllArgsConstructor +@Tag(name = "招采计划", description = "招采计划接口") +public class ProcurementPlanController extends BladeController { + + private final IProcurementPlanService procurementPlanService; + + /** + * 详情(含明细) + */ + @GetMapping("/detail") + @ApiOperationSupport(order = 1) + @Operation(summary = "详情", description = "传入id") + public R detail(@Parameter(name = "id", description = "主键", in = ParameterIn.QUERY, schema = @Schema(type = "integer")) @RequestParam Long id) { + ProcurementPlanVO detail = procurementPlanService.detail(id); + return R.data(detail); + } + + /** + * 分页 + */ + @GetMapping("/list") + @Parameters({ + @Parameter(name = "applicant", description = "申请人", in = ParameterIn.QUERY, schema = @Schema(type = "string")), + @Parameter(name = "billNo", description = "单据号", in = ParameterIn.QUERY, schema = @Schema(type = "string")), + @Parameter(name = "year", description = "年度", in = ParameterIn.QUERY, schema = @Schema(type = "string")) + }) + @ApiOperationSupport(order = 2) + @Operation(summary = "分页", description = "传入procurementPlan") + public R> list(@Parameter(hidden = true) @RequestParam Map procurementPlan, Query query) { + IPage pages = procurementPlanService.page(Condition.getPage(query), Condition.getQueryWrapper(procurementPlan, ProcurementPlan.class)); + return R.data(pages); + } + + /** + * 新增 + */ + @PostMapping("/save") + @ApiOperationSupport(order = 3) + @Operation(summary = "新增", description = "传入procurementPlan") + public R save(@RequestBody ProcurementPlanVO procurementPlanVO) { + return R.status(procurementPlanService.submit(procurementPlanVO)); + } + + /** + * 修改 + */ + @PostMapping("/update") + @ApiOperationSupport(order = 4) + @Operation(summary = "修改", description = "传入procurementPlan") + public R update(@RequestBody ProcurementPlanVO procurementPlanVO) { + return R.status(procurementPlanService.submit(procurementPlanVO)); + } + + /** + * 新增或修改 + */ + @PostMapping("/submit") + @ApiOperationSupport(order = 5) + @Operation(summary = "新增或修改", description = "传入procurementPlan") + public R submit(@RequestBody ProcurementPlanVO procurementPlanVO) { + return R.status(procurementPlanService.submit(procurementPlanVO)); + } + + /** + * 删除 + */ + @PostMapping("/remove") + @ApiOperationSupport(order = 6) + @Operation(summary = "逻辑删除", description = "传入ids") + public R remove(@Parameter(name = "ids", description = "主键集合", in = ParameterIn.QUERY, schema = @Schema(type = "string")) @RequestParam String ids) { + boolean temp = procurementPlanService.deleteLogic(Func.toLongList(ids)); + return R.status(temp); + } + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.java new file mode 100644 index 0000000..890bd2b --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.java @@ -0,0 +1,56 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.springblade.zhaocai.pojo.entity.ProcurementPlanDetail; + +import java.util.List; + +/** + * 招采计划明细 Mapper 接口 + * + * @author Chill + */ +public interface ProcurementPlanDetailMapper extends BaseMapper { + + /** + * 根据主表ID查询明细列表 + * + * @param planId 主表ID + * @return 明细列表 + */ + List selectByPlanId(Long planId); + + /** + * 根据主表ID删除明细 + * + * @param planId 主表ID + * @return 影响行数 + */ + int deleteByPlanId(Long planId); + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.xml b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.xml new file mode 100644 index 0000000..42da3ca --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanDetailMapper.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + id, tenant_id, create_user, create_time, update_user, update_time, status, is_deleted, + plan_id, year, plan, plan_type, sort_no, project_name, project_type, contact_person, + apply_date, expect_date, project_attr, material_detail + + + + + + delete from blade_procurement_plan_detail where plan_id = #{planId} + + + diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.java new file mode 100644 index 0000000..f9297d4 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.java @@ -0,0 +1,50 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.zhaocai.pojo.entity.ProcurementPlan; + +import java.util.List; + +/** + * 招采计划 Mapper 接口 + * + * @author Chill + */ +public interface ProcurementPlanMapper extends BaseMapper { + + /** + * 自定义分页 + * + * @param page + * @param procurementPlan + * @return + */ + List selectProcurementPlanPage(IPage page, ProcurementPlan procurementPlan); + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.xml b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.xml new file mode 100644 index 0000000..921f109 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/mapper/ProcurementPlanMapper.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select + id, tenant_id, create_user, create_dept, create_time, update_user, update_time, status, is_deleted, + applicant, apply_time, phone, company, bill_no, year, dept, apply_company, estimated_amount, amount_upper, remark + + + + + diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanDetailService.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanDetailService.java new file mode 100644 index 0000000..562cd75 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanDetailService.java @@ -0,0 +1,56 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import org.springblade.zhaocai.pojo.entity.ProcurementPlanDetail; + +import java.util.List; + +/** + * 招采计划明细服务类 + * + * @author Chill + */ +public interface IProcurementPlanDetailService extends IService { + + /** + * 根据主表ID查询明细列表 + * + * @param planId 主表ID + * @return 明细列表 + */ + List listByPlanId(Long planId); + + /** + * 根据主表ID删除明细 + * + * @param planId 主表ID + * @return 是否成功 + */ + boolean removeByPlanId(Long planId); + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanService.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanService.java new file mode 100644 index 0000000..17a4054 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/IProcurementPlanService.java @@ -0,0 +1,65 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import org.springblade.core.mp.base.BaseService; +import org.springblade.zhaocai.pojo.entity.ProcurementPlan; +import org.springblade.zhaocai.pojo.vo.ProcurementPlanVO; + +/** + * 招采计划服务类 + * + * @author Chill + */ +public interface IProcurementPlanService extends BaseService { + + /** + * 自定义分页 + * + * @param page + * @param procurementPlan + * @return + */ + IPage selectProcurementPlanPage(IPage page, ProcurementPlan procurementPlan); + + /** + * 详情(含明细) + * + * @param id 主键 + * @return 招采计划VO + */ + ProcurementPlanVO detail(Long id); + + /** + * 提交(新增或修改,含明细) + * + * @param procurementPlanVO 招采计划VO + * @return 是否成功 + */ + boolean submit(ProcurementPlanVO procurementPlanVO); + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanDetailServiceImpl.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanDetailServiceImpl.java new file mode 100644 index 0000000..d7d5bcd --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanDetailServiceImpl.java @@ -0,0 +1,58 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springblade.zhaocai.mapper.ProcurementPlanDetailMapper; +import org.springblade.zhaocai.pojo.entity.ProcurementPlanDetail; +import org.springblade.zhaocai.service.IProcurementPlanDetailService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 招采计划明细服务实现类 + * + * @author Chill + */ +@Service +public class ProcurementPlanDetailServiceImpl extends ServiceImpl implements IProcurementPlanDetailService { + + @Override + public List listByPlanId(Long planId) { + return baseMapper.selectList(Wrappers.lambdaQuery() + .eq(ProcurementPlanDetail::getPlanId, planId) + .orderByAsc(ProcurementPlanDetail::getSortNo)); + } + + @Override + public boolean removeByPlanId(Long planId) { + return baseMapper.delete(Wrappers.lambdaQuery() + .eq(ProcurementPlanDetail::getPlanId, planId)) > 0; + } + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanServiceImpl.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanServiceImpl.java new file mode 100644 index 0000000..0e2202c --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/service/impl/ProcurementPlanServiceImpl.java @@ -0,0 +1,102 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import lombok.AllArgsConstructor; +import org.springblade.core.mp.base.BaseServiceImpl; +import org.springblade.core.tool.utils.Func; +import org.springblade.zhaocai.mapper.ProcurementPlanMapper; +import org.springblade.zhaocai.pojo.entity.ProcurementPlan; +import org.springblade.zhaocai.pojo.entity.ProcurementPlanDetail; +import org.springblade.zhaocai.pojo.vo.ProcurementPlanDetailVO; +import org.springblade.zhaocai.pojo.vo.ProcurementPlanVO; +import org.springblade.zhaocai.service.IProcurementPlanDetailService; +import org.springblade.zhaocai.service.IProcurementPlanService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +/** + * 招采计划服务实现类 + * + * @author Chill + */ +@Service +@AllArgsConstructor +public class ProcurementPlanServiceImpl extends BaseServiceImpl implements IProcurementPlanService { + + private final IProcurementPlanDetailService procurementPlanDetailService; + + @Override + public IPage selectProcurementPlanPage(IPage page, ProcurementPlan procurementPlan) { + return page.setRecords(baseMapper.selectProcurementPlanPage(page, procurementPlan)); + } + + @Override + public ProcurementPlanVO detail(Long id) { + ProcurementPlanVO vo = new ProcurementPlanVO(); + ProcurementPlan plan = baseMapper.selectById(id); + if (plan == null) { + return vo; + } + Func.copy(plan, vo); + List detailList = procurementPlanDetailService.listByPlanId(id); + vo.setDetailList(detailList.stream().map(detail -> { + ProcurementPlanDetailVO detailVO = new ProcurementPlanDetailVO(); + Func.copy(detail, detailVO); + return detailVO; + }).toList()); + return vo; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean submit(ProcurementPlanVO procurementPlanVO) { + // 保存主表 + boolean result = saveOrUpdate(procurementPlanVO); + Long planId = procurementPlanVO.getId(); + // 删除旧明细 + procurementPlanDetailService.removeByPlanId(planId); + // 保存新明细 + List detailVOList = procurementPlanVO.getDetailList(); + if (Func.isNotEmpty(detailVOList)) { + List detailList = detailVOList.stream().map(detailVO -> { + ProcurementPlanDetail detail = new ProcurementPlanDetail(); + Func.copy(detailVO, detail); + detail.setPlanId(planId); + return detail; + }).toList(); + for (int i = 0; i < detailList.size(); i++) { + detailList.get(i).setSortNo(i + 1); + } + procurementPlanDetailService.saveBatch(detailList); + } + return result; + } + +} diff --git a/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/wrapper/ProcurementPlanWrapper.java b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/wrapper/ProcurementPlanWrapper.java new file mode 100644 index 0000000..f9281da --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/java/org/springblade/zhaocai/wrapper/ProcurementPlanWrapper.java @@ -0,0 +1,50 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement + * obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license + * from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party + * without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments + * from this software for such purposes. Copyright of this software + * remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software + * must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is + * not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.zhaocai.wrapper; + +import org.springblade.core.mp.support.BaseEntityWrapper; +import org.springblade.core.tool.utils.BeanUtil; +import org.springblade.zhaocai.pojo.entity.ProcurementPlan; +import org.springblade.zhaocai.pojo.vo.ProcurementPlanVO; + +/** + * 招采计划包装类 + * + * @author Chill + */ +public class ProcurementPlanWrapper extends BaseEntityWrapper { + + public static ProcurementPlanWrapper build() { + return new ProcurementPlanWrapper(); + } + + @Override + public ProcurementPlanVO entityVO(ProcurementPlan procurementPlan) { + ProcurementPlanVO procurementPlanVO = BeanUtil.copy(procurementPlan, ProcurementPlanVO.class); + return procurementPlanVO; + } + +} diff --git a/blade-service/blade-zhaocai/src/main/resources/application-dev.yml b/blade-service/blade-zhaocai/src/main/resources/application-dev.yml new file mode 100644 index 0000000..251a323 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/resources/application-dev.yml @@ -0,0 +1,28 @@ +#服务器端口 +server: + port: 8210 + +#Spring配置 +spring: + datasource: + #主数据源信息 + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://172.16.92.89:3306/bladex_zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: Aa123456 + #动态数据源信息 + dynamic: + druid: + #通用校验配置 + validation-query: select 1 + #设置默认的数据源或者数据源组,默认值即为master + primary: master + datasource: + slave: + druid: + #独立校验配置 + validation-query: select 1 + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://172.16.92.89:3306/bladex_zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: Aa123456 diff --git a/blade-service/blade-zhaocai/src/main/resources/application-prod.yml b/blade-service/blade-zhaocai/src/main/resources/application-prod.yml new file mode 100644 index 0000000..655b88a --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/resources/application-prod.yml @@ -0,0 +1,12 @@ +#服务器端口 +server: + port: 8210 + +#Spring配置 +spring: + datasource: + #主数据源信息 + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${blade.datasource.zhaocai.master.url} + username: ${blade.datasource.zhaocai.master.username} + password: ${blade.datasource.zhaocai.master.password} diff --git a/blade-service/blade-zhaocai/src/main/resources/application-test.yml b/blade-service/blade-zhaocai/src/main/resources/application-test.yml new file mode 100644 index 0000000..655b88a --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/resources/application-test.yml @@ -0,0 +1,12 @@ +#服务器端口 +server: + port: 8210 + +#Spring配置 +spring: + datasource: + #主数据源信息 + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${blade.datasource.zhaocai.master.url} + username: ${blade.datasource.zhaocai.master.username} + password: ${blade.datasource.zhaocai.master.password} diff --git a/blade-service/blade-zhaocai/src/main/resources/application.yml b/blade-service/blade-zhaocai/src/main/resources/application.yml new file mode 100644 index 0000000..a9a6969 --- /dev/null +++ b/blade-service/blade-zhaocai/src/main/resources/application.yml @@ -0,0 +1,10 @@ +#mybatis-plus配置 +mybatis-plus: + mapper-locations: classpath:org/springblade/**/mapper/*Mapper.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: org.springblade.**.entity + +#swagger扫描路径配置 +swagger: + base-packages: + - org.springblade diff --git a/blade-service/pom.xml b/blade-service/pom.xml new file mode 100644 index 0000000..fabaa76 --- /dev/null +++ b/blade-service/pom.xml @@ -0,0 +1,49 @@ + + + 4.0.0 + + + org.springblade + BladeX-Biz + ${revision} + + + blade-service + ${project.artifactId} + pom + BladeX 微服务集合 + + + blade-demo + blade-zhaocai + + + + + org.springblade + blade-biz-common + + + org.springblade + blade-starter-api-crypto + + + + org.springblade + blade-starter-tenant + + + + + + + + diff --git a/doc/nacos/blade-dev.yaml b/doc/nacos/blade-dev.yaml new file mode 100644 index 0000000..b9d312b --- /dev/null +++ b/doc/nacos/blade-dev.yaml @@ -0,0 +1,84 @@ +#spring配置 +spring: + data: + redis: + ##redis 单机环境配置 + host: 172.16.92.89 + port: 6379 + password: + database: 0 + ssl: + enabled: false + ##redis 集群环境配置 + #cluster: + # nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003 + # commandTimeout: 5000 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + #driver-class-name: org.postgresql.Driver + #driver-class-name: oracle.jdbc.OracleDriver + #driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + #driver-class-name: dm.jdbc.driver.DmDriver + #driver-class-name: com.yashandb.jdbc.Driver + #driver-class-name: com.kingbase8.Driver + druid: + # MySql、PostgreSQL、SqlServer、DaMeng校验 + validation-query: select 1 + # Oracle、YashanDB校验 + #oracle: true + #validation-query: select 1 from dual + +#项目模块集中配置 +blade: + #分布式锁配置 + lock: + enabled: false + address: redis://127.0.0.1:6379 + #多团队协作服务配置 + loadbalancer: + #开启配置 + enabled: true + #负载均衡优先调用的ip段 + prior-ip-pattern: + - 192.168.0.* + - 127.0.0.1 + #通用开发生产环境数据库地址(特殊情况可在对应的子工程里配置覆盖) + datasource: + dev: + # MySql + url: jdbc:mysql://172.19.2.126:30306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: sypm6666 + zhaocai: + master: + url: jdbc:mysql://172.19.2.126:30306/bladex-zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: sypm6666 + slave: + url: jdbc:mysql://172.19.2.126:30306/bladex-zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: sypm6666 + # PostgreSQL + #url: jdbc:postgresql://127.0.0.1:5432/bladex + #username: postgres + #password: 123456 + # Oracle + #url: jdbc:oracle:thin:@//127.0.0.1:1521/ORCLPDB + #username: BLADEX + #password: BLADEX + # SqlServer + #url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=bladex + #username: bladex + #password: bladex + # DaMeng + #url: jdbc:dm://127.0.0.1:5236/BLADEX?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8 + #username: BLADEX + #password: BLADEX + # YashanDB + #url: jdbc:yasdb://127.0.0.1:1688/BLADEX + #username: BLADEX + #password: BLADEX + # KingbaseES + #url: jdbc:kingbase8://localhost:4321/bladex + #username: kingbase + #password: 123456 diff --git a/doc/nacos/blade-prod.yaml b/doc/nacos/blade-prod.yaml new file mode 100644 index 0000000..2d13706 --- /dev/null +++ b/doc/nacos/blade-prod.yaml @@ -0,0 +1,56 @@ +#spring配置 +spring: + data: + redis: + ##redis 单机环境配置 + ##将docker脚本部署的redis服务映射为宿主机ip + ##生产环境推荐使用阿里云高可用redis服务并设置密码 + host: 192.168.0.188 + port: 3379 + password: + database: 0 + ssl: + enabled: false + ##redis 集群环境配置 + #cluster: + # nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003 + # commandTimeout: 5000 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + #driver-class-name: org.postgresql.Driver + #driver-class-name: oracle.jdbc.OracleDriver + #driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + #driver-class-name: dm.jdbc.driver.DmDriver + #driver-class-name: com.yashandb.jdbc.Driver + #driver-class-name: com.kingbase8.Driver + druid: + # MySql、PostgreSQL、SqlServer、DaMeng校验 + validation-query: select 1 + # Oracle、YashanDB校验 + #oracle: true + #validation-query: select 1 from dual + +#项目模块集中配置 +blade: + #分布式锁配置 + lock: + ##是否启用分布式锁 + enabled: false + ##将docker脚本部署的redis服务映射为宿主机ip + ##生产环境推荐使用阿里云高可用redis服务并设置密码 + address: redis://192.168.0.188:3379 + #通用开发生产环境数据库地址(特殊情况可在对应的子工程里配置覆盖) + datasource: + prod: + url: jdbc:mysql://192.168.0.188:3306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: root + zhaocai: + master: + url: jdbc:mysql://192.168.0.188:3306/bladex-zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: root + slave: + url: jdbc:mysql://192.168.0.188:3306/bladex-zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: root diff --git a/doc/nacos/blade-test.yaml b/doc/nacos/blade-test.yaml new file mode 100644 index 0000000..f132471 --- /dev/null +++ b/doc/nacos/blade-test.yaml @@ -0,0 +1,54 @@ +#spring配置 +spring: + data: + redis: + ##redis 单机环境配置 + ##将docker脚本部署的redis服务映射为宿主机ip + host: 192.168.0.188 + port: 3379 + password: + database: 0 + ssl: + enabled: false + ##redis 集群环境配置 + #cluster: + # nodes: 127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003 + # commandTimeout: 5000 + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + #driver-class-name: org.postgresql.Driver + #driver-class-name: oracle.jdbc.OracleDriver + #driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver + #driver-class-name: dm.jdbc.driver.DmDriver + #driver-class-name: com.yashandb.jdbc.Driver + #driver-class-name: com.kingbase8.Driver + druid: + # MySql、PostgreSQL、SqlServer、DaMeng校验 + validation-query: select 1 + # Oracle、YashanDB校验 + #oracle: true + #validation-query: select 1 from dual + +#项目模块集中配置 +blade: + #分布式锁配置 + lock: + ##是否启用分布式锁 + enabled: false + ##将docker脚本部署的redis服务映射为宿主机ip + address: redis://192.168.0.188:3379 + #通用开发生产环境数据库地址(特殊情况可在对应的子工程里配置覆盖) + datasource: + test: + url: jdbc:mysql://172.19.2.126:30306/bladex?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: sypm6666 + zhaocai: + master: + url: jdbc:mysql://172.19.2.126:30306/bladex-zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: sypm6666 + slave: + url: jdbc:mysql://172.19.2.126:30306/bladex-zhaocai?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&tinyInt1isBit=false&allowMultiQueries=true&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true + username: root + password: sypm6666 diff --git a/doc/nacos/blade.yaml b/doc/nacos/blade.yaml new file mode 100644 index 0000000..d71da14 --- /dev/null +++ b/doc/nacos/blade.yaml @@ -0,0 +1,249 @@ +#服务器配置 +server: + undertow: + threads: + # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 + io: 16 + # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载 + worker: 400 + # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理 + buffer-size: 1024 + # 是否分配的直接内存 + direct-buffers: true + +#spring配置 +spring: + cloud: + nacos: + discovery: + # 禁用云命名空间解析,防止和云平台环境变量冲突 + isUseCloudNamespaceParsing: false + config: + # 禁用云命名空间解析,防止和云平台环境变量冲突 + isUseCloudNamespaceParsing: false + sentinel: + eager: true + devtools: + restart: + log-condition-evaluation-delta: false + livereload: + port: 23333 + +#feign配置 +feign: + sentinel: + enabled: true + okhttp: + enabled: true + httpclient: + enabled: false + +#对外暴露端口 +management: + endpoints: + web: + exposure: + include: "*" + endpoint: + health: + show-details: always + +# mybatis +mybatis-plus: + mapper-locations: classpath:org/springblade/**/mapper/*Mapper.xml + #实体扫描,多个package用逗号或者分号分隔 + typeAliasesPackage: org.springblade.**.entity + #typeEnumsPackage: org.springblade.dashboard.entity.enums + global-config: + # 关闭MP3.0自带的banner + banner: false + db-config: + #主键类型 0:"数据库ID自增", 1:"不操作", 2:"用户输入ID",3:"数字型snowflake", 4:"全局唯一ID UUID", 5:"字符串型snowflake"; + id-type: assign_id + #字段策略 + insert-strategy: not_null + update-strategy: not_null + where-strategy: not_null + #驼峰下划线转换 + table-underline: true + # 逻辑删除配置 + # 逻辑删除全局值(1表示已删除,这也是Mybatis Plus的默认配置) + logic-delete-value: 1 + # 逻辑未删除全局值(0表示未删除,这也是Mybatis Plus的默认配置) + logic-not-delete-value: 0 + configuration: + map-underscore-to-camel-case: true + cache-enabled: false + jdbc-type-for-null: 'null' + +#springdoc-openapi配置 +springdoc: + default-flat-param-object: true + +#knife4j配置 +knife4j: + #基础认证 + basic: + enable: false + username: blade + password: blade + #增强配置 + setting: + enableSwaggerModels: true + enableDocumentManage: true + enableHost: false + enableHostText: http://localhost + enableRequestCache: true + enableFilterMultipartApis: false + enableFilterMultipartApiMethodType: POST + language: zh_cn + enableFooter: false + enableFooterCustom: true + footerCustomContent: Copyright © 2026 BladeX All Rights Reserved + +#swagger公共信息 +swagger: + title: BladeX 接口文档系统 + description: BladeX 接口文档系统 + version: 4.9.0.RELEASE + license: Powered By BladeX + license-url: https://bladex.cn + terms-of-service-url: https://bladex.cn + contact: + name: 翼宿 + email: bladejava@qq.com + url: https://gitee.com/smallc + +#blade配置 +blade: + #oauth2配置 + oauth2: + #启用 oauth2 + enabled: true + #使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2验签,需和前端保持一致 + public-key: ${BLADE_OAUTH2_PUBLIC_KEY} + #使用 @org.springblade.test.Sm2KeyGenerator 获取,用于国密sm2解密,前端无需配置 + private-key: ${BLADE_OAUTH2_PRIVATE_KEY} + #token配置 + token: + #是否有状态 + state: false + #是否单用户登录 + single: false + #单用户登录范围 + single-level: all + #token签名 使用blade-auth服务 @org.springblade.test.SignKeyGenerator 获取 + sign-key: ${BLADE_TOKEN_SIGN_KEY} + #token加密 使用blade-auth服务 @org.springblade.test.CryptoKeyGenerator 获取 + crypto-key: ${BLADE_TOKEN_CRYPTO_KEY} + #超级密钥配置 + key: + #启用超级密钥 + enabled: false + #密钥加密 使用 @org.springblade.test.CryptoKeyGenerator 获取 + crypto-key: ${BLADE_KEY_CRYPTO_KEY} + #接口配置 + api: + #报文加密配置 + crypto: + #启用报文加密配置 + enabled: false + #使用blade-auth服务 @org.springblade.test.CryptoKeyGenerator 获取,需和前端保持一致 + aes-key: ${BLADE_API_CRYPTO_AES_KEY} + #使用blade-auth服务 @org.springblade.test.CryptoKeyGenerator 获取,需和前端保持一致 + des-key: ${BLADE_API_CRYPTO_DES_KEY} + #jackson配置 + jackson: + #null自动转空值 + null-to-empty: true + #大数字自动转字符串 + big-num-to-string: true + #支持text文本请求,与报文加密同时开启 + support-text-plain: false + #redis序列化方式 + redis: + serializer-type: protostuff + #日志配置 + log: + request: + #开启控制台请求日志 + enabled: true + #控制台请求日志忽略 + skip-url: + - /notice/list + - /wechat/** + #开启错误日志入库 + error-log: true + #xss配置 + xss: + enabled: true + skip-url: + - /wechat + - /qq + #安全框架配置 + secure: + #严格模式 + #缺失令牌字段则取消授权 + strict-token: true + #缺失请求头则取消授权 + strict-header: true + #接口放行 + skip-url: + - /test/** + #授权认证配置 + auth: + - method: ALL + pattern: /chat/wechat/** + expression: "hasAuth()" + - method: ALL + pattern: /chat/qq/** + expression: "hasStrictToken()" + - method: ALL + pattern: /chat/ding/** + expression: "hasStrictHeader()" + - method: ALL + pattern: /dashboard/notice + expression: "hasMenu('notice')" + - method: POST + pattern: /dashboard/upload + expression: "hasTimeAuth(9, 17)" + - method: POST + pattern: /dashboard/submit + expression: "hasAnyRole('administrator', 'admin', 'user')" + #基础认证配置 + basic: + - method: ALL + pattern: /dashboard/info + username: "blade" + password: "blade" + #动态签名认证配置 + sign: + - method: ALL + pattern: /dashboard/sign + crypto: "sha1" + #多终端认证配置 + client: + - client-id: sword + path-patterns: + - /sword/** + - client-id: saber + path-patterns: + - /saber/** + #多租户配置 + tenant: + #多租户增强 + enhance: true + #多租户授权保护 + license: false + #动态数据源功能 + dynamic-datasource: false + #动态数据源全局扫描 + dynamic-global: false + #多租户字段名 + column: tenant_id + #排除多租户逻辑 + exclude-tables: + - blade_user + #分库分表配置 + sharding: + enabled: false diff --git a/doc/nacos/routes/blade-gateway-dev.json b/doc/nacos/routes/blade-gateway-dev.json new file mode 100644 index 0000000..1fef883 --- /dev/null +++ b/doc/nacos/routes/blade-gateway-dev.json @@ -0,0 +1,28 @@ +[ + { + "id": "desk-route", + "order": 0, + "predicates": [ + { + "name": "Path", + "args": { + "pattern": "/blade-desk/**" + } + } + ], + "filters": [], + "uri": "lb://blade-desk-me" + }, + { + "id": "example-route", + "order": 0, + "predicates": [{ + "name": "Path", + "args": { + "pattern": "/example" + } + }], + "filters": [], + "uri": "http://www.example.com" + } +] diff --git a/doc/sql/blade_zhaocai_mysql.sql b/doc/sql/blade_zhaocai_mysql.sql new file mode 100644 index 0000000..e46650d --- /dev/null +++ b/doc/sql/blade_zhaocai_mysql.sql @@ -0,0 +1,67 @@ +-- ---------------------------- +-- 招采计划模块 MySQL 建表语句 +-- ---------------------------- + +-- ---------------------------- +-- Table structure for blade_procurement_plan +-- ---------------------------- +DROP TABLE IF EXISTS `blade_procurement_plan`; + +CREATE TABLE `blade_procurement_plan` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID', + `applicant` varchar(64) DEFAULT NULL COMMENT '申请人', + `apply_time` datetime DEFAULT NULL COMMENT '申请时间', + `phone` varchar(32) DEFAULT NULL COMMENT '联系电话', + `company` varchar(128) DEFAULT NULL COMMENT '所属公司', + `bill_no` varchar(64) DEFAULT NULL COMMENT '单据号', + `year` varchar(4) DEFAULT NULL COMMENT '年度', + `dept` varchar(128) DEFAULT NULL COMMENT '所属部门', + `apply_company` varchar(128) DEFAULT NULL COMMENT '申请所属公司', + `estimated_amount` decimal(18,2) DEFAULT '0.00' COMMENT '预估总金额(万元)', + `amount_upper` varchar(256) DEFAULT NULL COMMENT '预估总金额大写', + `remark` text COMMENT '备注', + `status` int(2) DEFAULT '1' COMMENT '状态', + `create_user` bigint(20) DEFAULT NULL COMMENT '创建人', + `create_dept` bigint(20) DEFAULT NULL COMMENT '创建部门', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '更新人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `is_deleted` int(2) DEFAULT '0' COMMENT '是否已删除', + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `uk_bill_no` (`bill_no`) USING BTREE, + KEY `idx_year` (`year`) USING BTREE, + KEY `idx_applicant` (`applicant`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='招采计划主表'; + + +-- ---------------------------- +-- Table structure for blade_procurement_plan_detail +-- ---------------------------- +DROP TABLE IF EXISTS `blade_procurement_plan_detail`; + +CREATE TABLE `blade_procurement_plan_detail` ( + `id` bigint(20) NOT NULL COMMENT '主键', + `tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID', + `plan_id` bigint(20) NOT NULL COMMENT '主表ID', + `year` varchar(4) DEFAULT NULL COMMENT '年度', + `plan` varchar(128) DEFAULT NULL COMMENT '计划', + `plan_type` varchar(64) DEFAULT NULL COMMENT '计划类型', + `sort_no` int(11) DEFAULT '0' COMMENT '序号', + `project_name` varchar(256) DEFAULT NULL COMMENT '项目名称', + `project_type` varchar(64) DEFAULT NULL COMMENT '项目类型', + `contact_person` varchar(64) DEFAULT NULL COMMENT '项目对接人', + `apply_date` date DEFAULT NULL COMMENT '计划申请日期', + `expect_date` date DEFAULT NULL COMMENT '期望使用/完成日期', + `project_attr` varchar(64) DEFAULT NULL COMMENT '项目属性', + `material_detail` varchar(512) DEFAULT NULL COMMENT '采购物料明细', + `status` int(2) DEFAULT '1' COMMENT '状态', + `create_user` bigint(20) DEFAULT NULL COMMENT '创建人', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_user` bigint(20) DEFAULT NULL COMMENT '更新人', + `update_time` datetime DEFAULT NULL COMMENT '更新时间', + `is_deleted` int(2) DEFAULT '0' COMMENT '是否已删除', + PRIMARY KEY (`id`) USING BTREE, + KEY `idx_plan_id` (`plan_id`) USING BTREE, + KEY `idx_year` (`year`) USING BTREE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='招采计划明细表'; diff --git a/doc/sql/seata/seata.sql b/doc/sql/seata/seata.sql new file mode 100644 index 0000000..aa558bf --- /dev/null +++ b/doc/sql/seata/seata.sql @@ -0,0 +1,79 @@ +/* + Navicat Premium Data Transfer + + Source Server : mysql_localhost + Source Server Type : MySQL + Source Server Version : 50723 + Source Host : localhost:3306 + Source Schema : seata + + Target Server Type : MySQL + Target Server Version : 50723 + File Encoding : 65001 + + Date: 10/02/2020 23:42:58 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for branch_table +-- ---------------------------- +DROP TABLE IF EXISTS `branch_table`; +CREATE TABLE `branch_table` ( + `branch_id` bigint(20) NOT NULL, + `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `transaction_id` bigint(20) NULL DEFAULT NULL, + `resource_group_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `branch_type` varchar(8) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `status` tinyint(4) NULL DEFAULT NULL, + `client_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `gmt_create` datetime(6) NULL DEFAULT NULL, + `gmt_modified` datetime(6) NULL DEFAULT NULL, + PRIMARY KEY (`branch_id`) USING BTREE, + INDEX `idx_xid`(`xid`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Table structure for global_table +-- ---------------------------- +DROP TABLE IF EXISTS `global_table`; +CREATE TABLE `global_table` ( + `xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `transaction_id` bigint(20) NULL DEFAULT NULL, + `status` tinyint(4) NOT NULL, + `application_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `transaction_service_group` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `transaction_name` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `timeout` int(11) NULL DEFAULT NULL, + `begin_time` bigint(20) NULL DEFAULT NULL, + `application_data` varchar(2000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `gmt_create` datetime(0) NULL DEFAULT NULL, + `gmt_modified` datetime(0) NULL DEFAULT NULL, + PRIMARY KEY (`xid`) USING BTREE, + INDEX `idx_gmt_modified_status`(`gmt_modified`, `status`) USING BTREE, + INDEX `idx_transaction_id`(`transaction_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Table structure for lock_table +-- ---------------------------- +DROP TABLE IF EXISTS `lock_table`; +CREATE TABLE `lock_table` ( + `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `transaction_id` bigint(20) NULL DEFAULT NULL, + `branch_id` bigint(20) NOT NULL, + `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `gmt_create` datetime(0) NULL DEFAULT NULL, + `gmt_modified` datetime(0) NULL DEFAULT NULL, + PRIMARY KEY (`row_key`) USING BTREE, + INDEX `idx_branch_id`(`branch_id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/doc/sql/seata/seata_order.sql b/doc/sql/seata/seata_order.sql new file mode 100644 index 0000000..1288cc7 --- /dev/null +++ b/doc/sql/seata/seata_order.sql @@ -0,0 +1,51 @@ +/* + Navicat Premium Data Transfer + + Source Server : mysql_localhost + Source Server Type : MySQL + Source Server Version : 50723 + Source Host : localhost:3306 + Source Schema : seata_order + + Target Server Type : MySQL + Target Server Version : 50723 + File Encoding : 65001 + + Date: 13/11/2019 18:12:16 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for tb_order +-- ---------------------------- +DROP TABLE IF EXISTS `tb_order`; +CREATE TABLE `tb_order` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `count` int(11) NULL DEFAULT 0, + `money` int(11) NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `branch_id` bigint(20) NOT NULL, + `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int(11) NOT NULL, + `log_created` datetime(0) NOT NULL, + `log_modified` datetime(0) NOT NULL, + `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/doc/sql/seata/seata_storage.sql b/doc/sql/seata/seata_storage.sql new file mode 100644 index 0000000..eb3427d --- /dev/null +++ b/doc/sql/seata/seata_storage.sql @@ -0,0 +1,57 @@ +/* + Navicat Premium Data Transfer + + Source Server : mysql_localhost + Source Server Type : MySQL + Source Server Version : 50723 + Source Host : localhost:3306 + Source Schema : seata_storage + + Target Server Type : MySQL + Target Server Version : 50723 + File Encoding : 65001 + + Date: 13/11/2019 18:12:33 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for tb_storage +-- ---------------------------- +DROP TABLE IF EXISTS `tb_storage`; +CREATE TABLE `tb_storage` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `commodity_code` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + `count` int(11) NULL DEFAULT 0, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `commodity_code`(`commodity_code`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +-- ---------------------------- +-- Records of tb_storage +-- ---------------------------- +BEGIN; +INSERT INTO `tb_storage` VALUES (1, 'product-1', 9999999), (2, 'product-2', 0); +COMMIT; + +-- ---------------------------- +-- Table structure for undo_log +-- ---------------------------- +DROP TABLE IF EXISTS `undo_log`; +CREATE TABLE `undo_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `branch_id` bigint(20) NOT NULL, + `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `rollback_info` longblob NOT NULL, + `log_status` int(11) NOT NULL, + `log_created` datetime(0) NOT NULL, + `log_modified` datetime(0) NOT NULL, + `ext` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..0dd0f8e --- /dev/null +++ b/pom.xml @@ -0,0 +1,300 @@ + + + 4.0.0 + + org.springblade + BladeX-Biz + ${revision} + pom + + + + 4.9.0.RELEASE + + 17 + 3.14.1 + 1.7.3 + UTF-8 + UTF-8 + + + 192.168.0.188 + admin + admin12345 + blade + false + 0.42.0 + + + + blade-biz-common + blade-example + blade-gateway + blade-service + blade-service-api + + + + + + org.springblade.platform + blade-bom + ${revision} + pom + import + + + org.springblade + blade-biz-common + ${revision} + + + + org.springblade + blade-demo-api + ${revision} + + + org.springblade + blade-zhaocai-api + ${revision} + + + + + + + + + + + + + + + + + + + + org.projectlombok + lombok + provided + + + + + ${project.name} + + + src/main/resources + + + src/main/java + + **/*.xml + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 3.2.4 + + ${project.build.finalName} + + + + org.projectlombok + lombok + + + + + + + repackage + + + + + + io.fabric8 + docker-maven-plugin + ${docker.fabric.version} + + true + + ${docker.username} + ${docker.password} + + ${docker.registry.url} + + + ${docker.namespace}/${project.build.finalName}:${project.version} + ${project.name} + + ${project.basedir}/Dockerfile + + + + + ${basedir}/target/${project.build.finalName}.jar + + + + + org.apache.maven.plugins + maven-antrun-plugin + 3.1.0 + + + package + + run + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.plugin.version} + + ${java.version} + ${java.version} + UTF-8 + + -parameters + + + + + org.codehaus.mojo + flatten-maven-plugin + ${maven.flatten.version} + + true + oss + + remove + remove + remove + remove + remove + remove + + + + + flatten + process-resources + + flatten + + + + flatten.clean + clean + + clean + + + + + + org.commonjava.maven.plugins + directory-maven-plugin + 1.0 + + + directories + + highest-basedir + + initialize + + project.root.directory + + + + + + + + + + aliyun-repos + Aliyun Public Repository + https://maven.aliyun.com/repository/public + + false + + + + bladex + BladeX Release Repository + https://center.javablade.com/api/packages/blade/maven + + + + + aliyun-plugin + Aliyun Public Plugin + https://maven.aliyun.com/repository/public + + false + + + + + + + bladex + BladeX Release Repository + https://center.javablade.com/api/packages/blade/maven + + + bladex + BladeX Snapshot Repository + https://center.javablade.com/api/packages/blade/maven + + + + diff --git a/script/docker/app/.env b/script/docker/app/.env new file mode 100644 index 0000000..ec7c6d0 --- /dev/null +++ b/script/docker/app/.env @@ -0,0 +1,2 @@ +REGISTER=192.168.0.188/blade +TAG=4.9.0.RELEASE diff --git a/script/docker/app/deploy.sh b/script/docker/app/deploy.sh new file mode 100644 index 0000000..c671b57 --- /dev/null +++ b/script/docker/app/deploy.sh @@ -0,0 +1,166 @@ +#!/bin/bash + +#使用说明,用来提示输入参数 +usage() { + echo "Usage: sh 执行脚本.sh [port|mount|base|monitor|modules|prometheus|alertmanager|stop|rm|rmiNoneTag]" + exit 1 +} + +#开启所需端口 +port(){ + #gateway + firewall-cmd --add-port=88/tcp --permanent + #web + firewall-cmd --add-port=8000/tcp --permanent + #nacos + firewall-cmd --add-port=8848/tcp --permanent + firewall-cmd --add-port=9848/tcp --permanent + firewall-cmd --add-port=9849/tcp --permanent + #sentinel + firewall-cmd --add-port=8858/tcp --permanent + #grafana + firewall-cmd --add-port=3000/tcp --permanent + #mysql + firewall-cmd --add-port=3306/tcp --permanent + #redis + firewall-cmd --add-port=3379/tcp --permanent + #admin + firewall-cmd --add-port=7002/tcp --permanent + #ureport + firewall-cmd --add-port=8108/tcp --permanent + #zipkin + firewall-cmd --add-port=9411/tcp --permanent + #prometheus + firewall-cmd --add-port=9090/tcp --permanent + #swagger + firewall-cmd --add-port=18000/tcp --permanent + #powerjob + firewall-cmd --add-port=7700/tcp --permanent + firewall-cmd --add-port=10086/tcp --permanent + firewall-cmd --add-port=10010/tcp --permanent + #firewalld + service firewalld restart +} + +##放置挂载文件 +mount(){ + #挂载配置文件 + if test ! -f "/docker/nginx/api/nginx.conf" ;then + mkdir -p /docker/nginx/api + cp nginx/api/nginx.conf /docker/nginx/api/nginx.conf + fi + if test ! -f "/docker/nginx/web/nginx.conf" ;then + mkdir -p /docker/nginx/web + cp nginx/web/nginx.conf /docker/nginx/web/nginx.conf + cp -r nginx/web/html /docker/nginx/web/html + fi + if test ! -f "/docker/nacos/conf/application.properties" ;then + mkdir -p /docker/nacos/conf + cp nacos/conf/application.properties /docker/nacos/conf/application.properties + fi + if test ! -f "/docker/prometheus/prometheus.yml" ;then + mkdir -p /docker/prometheus + cp prometheus/config/prometheus.yml /docker/prometheus/prometheus.yml + fi + if test ! -f "/docker/prometheus/rules/alert_rules.yml" ;then + mkdir -p /docker/prometheus/rules + cp prometheus/config/alert_rules.yml /docker/prometheus/rules/alert_rules.yml + fi + if test ! -f "/docker/grafana/grafana.ini" ;then + mkdir -p /docker/grafana + cp prometheus/config/grafana.ini /docker/grafana/grafana.ini + fi + if test ! -f "/docker/alertmanager/alertmanager.yml" ;then + mkdir -p /docker/alertmanager + cp prometheus/config/alertmanager.yml /docker/alertmanager/alertmanager.yml + fi + if test ! -f "/docker/alertmanager/templates/wechat.tmpl" ;then + mkdir -p /docker/alertmanager/templates + cp prometheus/config/wechat.tmpl /docker/alertmanager/templates/wechat.tmpl + fi + if test ! -f "/docker/webhook_dingtalk/dingtalk.yml" ;then + mkdir -p /docker/webhook_dingtalk + cp prometheus/config/dingtalk.yml /docker/webhook_dingtalk/dingtalk.yml + fi + #增加目录权限 + chmod -R 777 /docker/prometheus + chmod -R 777 /docker/grafana + chmod -R 777 /docker/alertmanager +} + +#启动基础模块 +base(){ + docker-compose up -d nacos sentinel seata-server web-nginx blade-nginx blade-redis powerjob-server +} + +#启动监控模块 +monitor(){ + docker-compose up -d blade-admin +} + +#启动程序模块 +modules(){ + docker-compose up -d blade-gateway1 blade-gateway2 blade-auth1 blade-auth2 blade-report blade-desk blade-system blade-log blade-flow blade-resource blade-job +} + +#启动普罗米修斯模块 +prometheus(){ + docker-compose up -d prometheus node-exporter mysqld-exporter cadvisor grafana +} + +#启动监听模块 +alertmanager(){ + docker-compose up -d alertmanager webhook-dingtalk +} + +#关闭所有模块 +stop(){ + docker-compose stop +} + +#删除所有模块 +rm(){ + docker-compose rm +} + +#删除Tag为空的镜像 +rmiNoneTag(){ + docker images|grep none|awk '{print $3}'|xargs docker rmi -f +} + +#根据输入参数,选择执行对应方法,不输入则执行使用说明 +case "$1" in +"port") + port +;; +"mount") + mount +;; +"base") + base +;; +"monitor") + monitor +;; +"modules") + modules +;; +"prometheus") + prometheus +;; +"alertmanager") + alertmanager +;; +"stop") + stop +;; +"rm") + rm +;; +"rmiNoneTag") + rmiNoneTag +;; +*) + usage +;; +esac diff --git a/script/docker/app/docker-compose.yml b/script/docker/app/docker-compose.yml new file mode 100644 index 0000000..2fbfee4 --- /dev/null +++ b/script/docker/app/docker-compose.yml @@ -0,0 +1,366 @@ +version: '3' +services: + + #################################################################################################### + ###=================================== 以下为中间件模块 =========================================### + #################################################################################################### + + nacos: + image: nacos/nacos-server:v3.1.1 + hostname: "nacos-standalone" + environment: + - NACOS_AUTH_ENABLE=true + - NACOS_AUTH_CACHE_ENABLE=true + - NACOS_AUTH_IDENTITY_KEY=nacos + - NACOS_AUTH_IDENTITY_VALUE=nacos + - NACOS_AUTH_TOKEN= # 请阅读官方文档了解规则后替换为自己的token:https://nacos.io/zh-cn/docs/v2/guide/user/auth.html + - MODE=standalone + - TZ=Asia/Shanghai + volumes: + - /docker/nacos/standalone-logs/:/home/nacos/logs + - /docker/nacos/conf/application.properties:/home/nacos/conf/application.properties + ports: + - 8848:8848 + - 9848:9848 + - 8080:8080 + networks: + blade_net: + ipv4_address: 172.30.0.48 + + sentinel: + image: bladex/sentinel-dashboard:1.8.6 + hostname: "sentinel" + environment: + - TZ=Asia/Shanghai + ports: + - 8858:8858 + restart: on-failure + networks: + blade_net: + ipv4_address: 172.30.0.58 + + seata-server: + image: seataio/seata-server:1.6.1 + hostname: "seata-server" + ports: + - 8091:8091 + environment: + - TZ=Asia/Shanghai + - SEATA_PORT=8091 + - STORE_MODE=file + networks: + blade_net: + ipv4_address: 172.30.0.68 + + blade-nginx: + image: nginx:stable-alpine-perl + hostname: "blade-nginx" + environment: + - TZ=Asia/Shanghai + ports: + - 88:88 + volumes: + - /docker/nginx/api/nginx.conf:/etc/nginx/nginx.conf + privileged: true + restart: always + networks: + - blade_net + + web-nginx: + image: nginx:stable-alpine-perl + hostname: "web-nginx" + environment: + - TZ=Asia/Shanghai + ports: + - 8000:8000 + volumes: + - /docker/nginx/web/html:/usr/share/nginx/html + - /docker/nginx/web/nginx.conf:/etc/nginx/nginx.conf + privileged: true + restart: always + networks: + - blade_net + + blade-redis: + image: redis:7-alpine + hostname: "blade-redis" + environment: + - TZ=Asia/Shanghai + ports: + - 3379:6379 + volumes: + - /docker/redis/data:/data + command: "redis-server --appendonly yes" + privileged: true + restart: always + networks: + - blade_net + + ##powerjob.network.external.address可配置为外部宿主机地址,更详细见:https://www.yuque.com/powerjob/guidence/deploy_server + powerjob-server: + container_name: powerjob-server + image: powerjob/powerjob-server:4.3.6 + restart: always + environment: + JVMOPTIONS: "-Xmx512m -Dpowerjob.network.external.address=172.30.0.70 -Dpowerjob.network.external.port.http=10010 -Dpowerjob.network.external.port.akka=10086" + PARAMS: "--spring.datasource.core.jdbc-url=jdbc:mysql://mysql服务ip:端口/powerjob-product?useUnicode=true&characterEncoding=UTF-8&useSSL=false --spring.datasource.core.username=mysql账号名 --spring.datasource.core.password=mysql密码 --oms.mongodb.enable=false" + ports: + - 7700:7700 + - 10086:10086 + - 10010:10010 + networks: + blade_net: + ipv4_address: 172.30.0.70 + + #################################################################################################### + ###================================= 以下为BladeX服务模块 =======================================### + #################################################################################################### + + blade-admin: + image: "${REGISTER}/blade-admin:${TAG}" + environment: + - TZ=Asia/Shanghai + ports: + - 7002:7002 + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.72 + + blade-gateway1: + image: "${REGISTER}/blade-gateway:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.81 + + blade-gateway2: + image: "${REGISTER}/blade-gateway:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.82 + + blade-auth1: + image: "${REGISTER}/blade-auth:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.91 + + blade-auth2: + image: "${REGISTER}/blade-auth:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.92 + + blade-report: + image: "${REGISTER}/blade-report:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + ports: + - 8108:8108 + networks: + blade_net: + ipv4_address: 172.30.0.98 + + blade-log: + image: "${REGISTER}/blade-log:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + - blade_net + + blade-desk: + image: "${REGISTER}/blade-desk:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + - blade_net + + blade-system: + image: "${REGISTER}/blade-system:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + - blade_net + + blade-flow: + image: "${REGISTER}/blade-flow:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + - blade_net + + blade-resource: + image: "${REGISTER}/blade-resource:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + - blade_net + + blade-job: + image: "${REGISTER}/blade-job:${TAG}" + environment: + - TZ=Asia/Shanghai + privileged: true + restart: always + networks: + - blade_net + + #################################################################################################### + ###=============================== 以下为Prometheus监控模块 =====================================### + #################################################################################################### + + prometheus: + image: prom/prometheus:v2.24.1 + hostname: "prometheus" + environment: + - TZ=Asia/Shanghai + ports: + - 9090:9090 + volumes: + - /docker/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml + - /docker/prometheus/rules:/etc/prometheus/rules + command: "--config.file=/etc/prometheus/prometheus.yml --web.enable-lifecycle" + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.90 + + node-exporter: + image: prom/node-exporter:v1.0.1 + hostname: "node-exporter" + environment: + - TZ=Asia/Shanghai + ports: + - 9190:9100 + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.93 + + mysqld-exporter: + image: prom/mysqld-exporter:v0.12.1 + hostname: "mysqld-exporter" + environment: + - TZ=Asia/Shanghai + # 需要先在mysql服务执行如下语句 + # ===================================================================================== + # === CREATE USER 'exporter'@'mysql服务ip' IDENTIFIED BY '密码'; === + # === GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'exporter'@'mysql服务ip'; === + # === flush privileges; === + # ===================================================================================== + - DATA_SOURCE_NAME=exporter:密码@(mysql服务ip:mysql服务端口)/ + ports: + - 9104:9104 + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.94 + + cadvisor: + image: google/cadvisor:v0.33.0 + hostname: "cadvisor" + environment: + - TZ=Asia/Shanghai + ports: + - 18080:8080 + volumes: + - /:/rootfs:ro + - /var/run:/var/run:rw + - /sys:/sys:ro + - /var/lib/docker/:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + command: "detach=true" + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.180 + + grafana: + image: grafana/grafana:7.3.7 + hostname: "grafana" + environment: + - TZ=Asia/Shanghai + - GF_SERVER_ROOT_URL=https://grafana.bladex.vip + - GF_SECURITY_ADMIN_PASSWORD=1qaz@WSX + ports: + - 3000:3000 + volumes: + - /docker/grafana/grafana.ini:/etc/grafana/grafana.ini + - /docker/grafana:/var/lib/grafana + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.30 + + alertmanager: + image: prom/alertmanager:v0.21.0 + hostname: "alertmanager" + environment: + - TZ=Asia/Shanghai + ports: + - 9093:9093 + volumes: + - /docker/alertmanager/alertmanager.yml:/etc/alertmanager/alertmanager.yml + - /docker/alertmanager/data:/etc/alertmanager/data + - /docker/alertmanager/templates:/etc/alertmanager/templates + command: "--config.file=/etc/alertmanager/alertmanager.yml --storage.path=/etc/alertmanager/data" + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.99 + + webhook-dingtalk: + image: timonwong/prometheus-webhook-dingtalk:v1.4.0 + hostname: "webhook-dingtalk" + environment: + - TZ=Asia/Shanghai + ports: + - 8060:8060 + command: "ding.profile=webhook_robot=https://oapi.dingtalk.com/robot/send?access_token=xxxxx" + privileged: true + restart: always + networks: + blade_net: + ipv4_address: 172.30.0.96 + +networks: + blade_net: + driver: bridge + ipam: + config: + - subnet: 172.30.0.0/16 diff --git a/script/docker/app/nacos/conf/application.properties b/script/docker/app/nacos/conf/application.properties new file mode 100644 index 0000000..971c82f --- /dev/null +++ b/script/docker/app/nacos/conf/application.properties @@ -0,0 +1,55 @@ +# spring +server.servlet.contextPath=${SERVER_SERVLET_CONTEXTPATH:/nacos} +server.contextPath=/nacos +server.port=${NACOS_APPLICATION_PORT:8848} +server.tomcat.accesslog.max-days=30 +server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i +server.tomcat.accesslog.enabled=${TOMCAT_ACCESSLOG_ENABLED:false} +server.error.include-message=ALWAYS +# default current work dir +server.tomcat.basedir=file:. +#*************** Config Module Related Configurations ***************# +### Deprecated configuration property, it is recommended to use `spring.sql.init.platform` replaced. +#spring.datasource.platform=${SPRING_DATASOURCE_PLATFORM:} +spring.sql.init.platform=${SPRING_DATASOURCE_PLATFORM:} +nacos.cmdb.dumpTaskInterval=3600 +nacos.cmdb.eventTaskInterval=10 +nacos.cmdb.labelTaskInterval=300 +nacos.cmdb.loadDataAtStart=false +db.num=${MYSQL_DATABASE_NUM:1} +db.url.0=jdbc:mysql://${MYSQL_SERVICE_HOST}:${MYSQL_SERVICE_PORT:3306}/${MYSQL_SERVICE_DB_NAME}?${MYSQL_SERVICE_DB_PARAM:characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false} +db.user.0=${MYSQL_SERVICE_USER} +db.password.0=${MYSQL_SERVICE_PASSWORD} +## DB connection pool settings +db.pool.config.connectionTimeout=${DB_POOL_CONNECTION_TIMEOUT:30000} +db.pool.config.validationTimeout=10000 +db.pool.config.maximumPoolSize=20 +db.pool.config.minimumIdle=2 +### The auth system to use, currently only 'nacos' and 'ldap' is supported: +nacos.core.auth.system.type=${NACOS_AUTH_SYSTEM_TYPE:nacos} +### worked when nacos.core.auth.system.type=nacos +### The token expiration in seconds: +nacos.core.auth.plugin.nacos.token.expire.seconds=${NACOS_AUTH_TOKEN_EXPIRE_SECONDS:18000} +### The default token: +nacos.core.auth.plugin.nacos.token.secret.key=${NACOS_AUTH_TOKEN:} +### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. +nacos.core.auth.caching.enabled=${NACOS_AUTH_CACHE_ENABLE:false} +nacos.core.auth.enable.userAgentAuthWhite=${NACOS_AUTH_USER_AGENT_AUTH_WHITE_ENABLE:false} +nacos.core.auth.server.identity.key=${NACOS_AUTH_IDENTITY_KEY:} +nacos.core.auth.server.identity.value=${NACOS_AUTH_IDENTITY_VALUE:} +## spring security config +### turn off security +nacos.security.ignore.urls=${NACOS_SECURITY_IGNORE_URLS:/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-fe/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/**} +# metrics for elastic search +management.metrics.export.elastic.enabled=false +management.metrics.export.influx.enabled=false +nacos.naming.distro.taskDispatchThreadCount=10 +nacos.naming.distro.taskDispatchPeriod=200 +nacos.naming.distro.batchSyncKeyCount=1000 +nacos.naming.distro.initDataRatio=0.9 +nacos.naming.distro.syncRetryDelay=5000 +nacos.naming.data.warmup=true +nacos.console.ui.enabled=true +nacos.core.param.check.enabled=true + + diff --git a/script/docker/app/nginx/api/nginx.conf b/script/docker/app/nginx/api/nginx.conf new file mode 100644 index 0000000..95d2ae5 --- /dev/null +++ b/script/docker/app/nginx/api/nginx.conf @@ -0,0 +1,77 @@ + +user root; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #gzip on; + + #include /etc/nginx/conf.d/*.conf; + + upstream gateway { + server 172.30.0.81; + server 172.30.0.82; + } + + upstream auth { + server 172.30.0.91:8100; + server 172.30.0.92:8100; + } + + server { + listen 88; + server_name gateway; + location / { + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_buffering off; + proxy_pass http://gateway/; + } + + location ~ ^/(api/)?actuator { + return 403; + } + } + + server { + listen 9000; + server_name auth; + location / { + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_buffering off; + proxy_pass http://auth/; + } + + location ~ ^/(api/)?actuator { + return 403; + } + } + +} diff --git a/script/docker/app/nginx/web/html/index.html b/script/docker/app/nginx/web/html/index.html new file mode 100644 index 0000000..5616b14 --- /dev/null +++ b/script/docker/app/nginx/web/html/index.html @@ -0,0 +1,10 @@ + + + + + Hello BladeX + + +

Hello BladeX !
+ + diff --git a/script/docker/app/nginx/web/nginx.conf b/script/docker/app/nginx/web/nginx.conf new file mode 100644 index 0000000..b8ae1f7 --- /dev/null +++ b/script/docker/app/nginx/web/nginx.conf @@ -0,0 +1,77 @@ + +user root; +worker_processes 1; + +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + + +events { + worker_connections 1024; +} + + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + + keepalive_timeout 65; + + #include /etc/nginx/conf.d/*.conf; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_comp_level 2; + gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml; + gzip_vary on; + gzip_proxied expired no-cache no-store private auth; + gzip_disable "MSIE [1-6]\."; + + upstream gateway { + server 172.30.0.81; + server 172.30.0.82; + } + + + server { + listen 8000; + server_name web; + root /usr/share/nginx/html; + + location /{ + index index.html; + error_page 404 /index.html; + } + + location ~ ^/(api/)?actuator { + return 403; + } + + location ^~ /oauth/redirect { + rewrite ^(.*)$ /index.html break; + } + + location ^~ /api/ { + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_buffering off; + rewrite ^/api/(.*)$ /$1 break; + proxy_pass http://gateway/; + } + } + + +} diff --git a/script/docker/app/prometheus/config/alert_rules.yml b/script/docker/app/prometheus/config/alert_rules.yml new file mode 100644 index 0000000..db23fdd --- /dev/null +++ b/script/docker/app/prometheus/config/alert_rules.yml @@ -0,0 +1,118 @@ +groups: + - name: alert_rules + rules: + - alert: CpuUsageAlertWarning + expr: sum(avg(irate(node_cpu_seconds_total{mode!='idle'}[5m])) without (cpu)) by (instance) > 0.60 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} CPU usage high" + description: "{{ $labels.instance }} CPU usage above 60% (current value: {{ $value }})" + - alert: CpuUsageAlertSerious + #expr: sum(avg(irate(node_cpu_seconds_total{mode!='idle'}[5m])) without (cpu)) by (instance) > 0.85 + expr: (100 - (avg by (instance) (irate(node_cpu_seconds_total{job=~".*",mode="idle"}[5m])) * 100)) > 85 + for: 3m + labels: + level: serious + annotations: + summary: "Instance {{ $labels.instance }} CPU usage high" + description: "{{ $labels.instance }} CPU usage above 85% (current value: {{ $value }})" + - alert: MemUsageAlertWarning + expr: avg by(instance) ((1 - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) / node_memory_MemTotal_bytes) * 100) > 70 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} MEM usage high" + description: "{{$labels.instance}}: MEM usage is above 70% (current value is: {{ $value }})" + - alert: MemUsageAlertSerious + expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes)/node_memory_MemTotal_bytes > 0.90 + for: 3m + labels: + level: serious + annotations: + summary: "Instance {{ $labels.instance }} MEM usage high" + description: "{{ $labels.instance }} MEM usage above 90% (current value: {{ $value }})" + - alert: DiskUsageAlertWarning + expr: (1 - node_filesystem_free_bytes{fstype!="rootfs",mountpoint!="",mountpoint!~"/(run|var|sys|dev).*"} / node_filesystem_size_bytes) * 100 > 80 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Disk usage high" + description: "{{$labels.instance}}: Disk usage is above 80% (current value is: {{ $value }})" + - alert: DiskUsageAlertSerious + expr: (1 - node_filesystem_free_bytes{fstype!="rootfs",mountpoint!="",mountpoint!~"/(run|var|sys|dev).*"} / node_filesystem_size_bytes) * 100 > 90 + for: 3m + labels: + level: serious + annotations: + summary: "Instance {{ $labels.instance }} Disk usage high" + description: "{{$labels.instance}}: Disk usage is above 90% (current value is: {{ $value }})" + - alert: NodeFileDescriptorUsage + expr: avg by (instance) (node_filefd_allocated{} / node_filefd_maximum{}) * 100 > 60 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} File Descriptor usage high" + description: "{{$labels.instance}}: File Descriptor usage is above 60% (current value is: {{ $value }})" + - alert: NodeLoad15 + expr: avg by (instance) (node_load15{}) > 80 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Load15 usage high" + description: "{{$labels.instance}}: Load15 is above 80 (current value is: {{ $value }})" + - alert: NodeAgentStatus + expr: avg by (instance) (up{}) == 0 + for: 2m + labels: + level: warning + annotations: + summary: "{{$labels.instance}}: has been down" + description: "{{$labels.instance}}: Node_Exporter Agent is down (current value is: {{ $value }})" + - alert: NodeProcsBlocked + expr: avg by (instance) (node_procs_blocked{}) > 10 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Process Blocked usage high" + description: "{{$labels.instance}}: Node Blocked Procs detected! above 10 (current value is: {{ $value }})" + - alert: NetworkTransmitRate + #expr: avg by (instance) (floor(irate(node_network_transmit_bytes_total{device="ens192"}[2m]) / 1024 / 1024)) > 50 + expr: avg by (instance) (floor(irate(node_network_transmit_bytes_total{}[2m]) / 1024 / 1024 * 8 )) > 40 + for: 1m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Network Transmit Rate usage high" + description: "{{$labels.instance}}: Node Transmit Rate (Upload) is above 40Mbps/s (current value is: {{ $value }}Mbps/s)" + - alert: NetworkReceiveRate + #expr: avg by (instance) (floor(irate(node_network_receive_bytes_total{device="ens192"}[2m]) / 1024 / 1024)) > 50 + expr: avg by (instance) (floor(irate(node_network_receive_bytes_total{}[2m]) / 1024 / 1024 * 8 )) > 40 + for: 1m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Network Receive Rate usage high" + description: "{{$labels.instance}}: Node Receive Rate (Download) is above 40Mbps/s (current value is: {{ $value }}Mbps/s)" + - alert: DiskReadRate + expr: avg by (instance) (floor(irate(node_disk_read_bytes_total{}[2m]) / 1024 )) > 200 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Disk Read Rate usage high" + description: "{{$labels.instance}}: Node Disk Read Rate is above 200KB/s (current value is: {{ $value }}KB/s)" + - alert: DiskWriteRate + expr: avg by (instance) (floor(irate(node_disk_written_bytes_total{}[2m]) / 1024 / 1024 )) > 20 + for: 2m + labels: + level: warning + annotations: + summary: "Instance {{ $labels.instance }} Disk Write Rate usage high" + description: "{{$labels.instance}}: Node Disk Write Rate is above 20MB/s (current value is: {{ $value }}MB/s)" diff --git a/script/docker/app/prometheus/config/alertmanager.yml b/script/docker/app/prometheus/config/alertmanager.yml new file mode 100644 index 0000000..09cfe38 --- /dev/null +++ b/script/docker/app/prometheus/config/alertmanager.yml @@ -0,0 +1,56 @@ +global: + # 在没有报警的情况下声明为已解决的时间 + resolve_timeout: 5m + # 配置邮件发送信息 + smtp_smarthost: 'smtp.163.com:25' + # 邮箱地址 + smtp_from: 'bladejava@163.com' + # 邮箱地址 + smtp_auth_username: 'bladejava@163.com' + # 邮箱授权码,需要自行开启设置,非邮箱密码 + smtp_auth_password: 'xxxxxxxx' + # 邮箱地址 + smtp_hello: 'bladejava@163.com' + smtp_require_tls: false + +templates: + # 告警模板文件 + - "/etc/alertmanager/templates/wechat.tmpl" + +route: + # 接收到告警后到自定义分组 + group_by: ["alertname"] + # 分组创建后初始化等待时长 + group_wait: 10s + # 告警信息发送之前的等待时长 + group_interval: 30s + # 重复报警的间隔时长 + repeat_interval: 5m + # 默认消息接收 + receiver: "wechat" + +receivers: + # 微信 + - name: "wechat" + wechat_configs: + # 是否发送恢复信息 + - send_resolved: true + # 填写应用 AgentId + agent_id: "1000002" + # 填写应用 Secret + api_secret: "jxxxxxxxxxxxxxxxxxxxc" + # 填写企业 ID + corp_id: "wwxxxxxxxxxxx01d" + # 填写接收消息的群体 + to_user: "@all" + # 钉钉 + - name: 'dingtalk' + webhook_configs: + # prometheus-webhook-dingtalk服务的地址 + - url: http://172.30.0.96:8060/dingtalk/webhook_robot/send + send_resolved: true + # 邮件 + - name: 'email' + email_configs: + - to: 'your email' + send_resolved: true diff --git a/script/docker/app/prometheus/config/dingtalk.yml b/script/docker/app/prometheus/config/dingtalk.yml new file mode 100644 index 0000000..9fd668b --- /dev/null +++ b/script/docker/app/prometheus/config/dingtalk.yml @@ -0,0 +1,12 @@ +timeout: 5s + +targets: + webhook_robot: + # 钉钉机器人创建后的webhook地址 + url: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxx + webhook_mention_all: + # 钉钉机器人创建后的webhook地址 + url: https://oapi.dingtalk.com/robot/send?access_token=xxxxxxxxxxxx + # 提醒全员 + mention: + all: true diff --git a/script/docker/app/prometheus/config/grafana.ini b/script/docker/app/prometheus/config/grafana.ini new file mode 100644 index 0000000..b1e5ac0 --- /dev/null +++ b/script/docker/app/prometheus/config/grafana.ini @@ -0,0 +1,849 @@ +##################### Grafana Configuration Example ##################### +# +# Everything has defaults so you only need to uncomment things you want to +# change + +# possible values : production, development +;app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +;instance_name = ${HOSTNAME} + +#################################### Paths #################################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +;data = /var/lib/grafana + +# Temporary files in `data` directory older than given duration will be removed +;temp_data_lifetime = 24h + +# Directory where grafana can store logs +;logs = /var/log/grafana + +# Directory where grafana will automatically scan and look for plugins +;plugins = /var/lib/grafana/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +;provisioning = conf/provisioning + +#################################### Server #################################### +[server] +# Protocol (http, https, h2, socket) +;protocol = http + +# The ip address to bind to, empty will bind to all interfaces +;http_addr = + +# The http port to use +;http_port = 3000 + +# The public facing domain name used to access grafana from a browser +;domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +;enforce_domain = false + +# The full public facing url you use in browser, used for redirects and emails +# If you use reverse proxy and sub path specify full url (with sub path) +;root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +;serve_from_sub_path = false + +# Log web requests +;router_logging = false + +# the path relative working path +;static_root_path = public + +# enable gzip +;enable_gzip = false + +# https certs & key file +;cert_file = +;cert_key = + +# Unix socket path +;socket = + +#################################### Database #################################### +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url properties. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +;type = sqlite3 +;host = 127.0.0.1:3306 +;name = grafana +;user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +;password = + +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +;url = + +# For "postgres" only, either "disable", "require" or "verify-full" +;ssl_mode = disable + +;ca_cert_path = +;client_key_path = +;client_cert_path = +;server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +;path = grafana.db + +# Max idle conn setting default is 2 +;max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +;max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +;conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +;log_queries = + +# For "sqlite3" only. cache mode setting used for connecting to the database. (private, shared) +;cache_mode = private + +#################################### Cache server ############################# +[remote_cache] +# Either "redis", "memcached" or "database" default is "database" +;type = database + +# cache connectionstring options +# database: will use Grafana primary database. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# memcache: 127.0.0.1:11211 +;connstr = + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +;logging = false + +# How long the data proxy waits before timing out, default is 30 seconds. +# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. +;timeout = 30 + +# How many seconds the data proxy waits before sending a keepalive probe request. +;keep_alive_seconds = 30 + +# How many seconds the data proxy waits for a successful TLS Handshake before timing out. +;tls_handshake_timeout_seconds = 10 + +# How many seconds the data proxy will wait for a server's first response headers after +# fully writing the request headers if the request has an "Expect: 100-continue" +# header. A value of 0 will result in the body being sent immediately, without +# waiting for the server to approve. +;expect_continue_timeout_seconds = 1 + +# The maximum number of idle connections that Grafana will keep alive. +;max_idle_connections = 100 + +# How many seconds the data proxy keeps an idle connection open before timing out. +;idle_conn_timeout_seconds = 90 + +# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request, default is false. +;send_user_header = false + +#################################### Analytics #################################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +;reporting_enabled = true + +# Set to false to disable all checks to https://grafana.net +# for new versions (grafana itself and plugins), check is used +# in some UI views to notify that grafana or plugin update exists +# This option does not cause any auto updates, nor send any information +# only a GET request to http://grafana.com to get latest versions +;check_for_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +;google_analytics_ua_id = + +# Google Tag Manager ID, only enabled if you specify an id here +;google_tag_manager_id = + +#################################### Security #################################### +[security] +# disable creation of admin user on first start of grafana +;disable_initial_admin_creation = false + +# default admin user, created on startup +;admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +;admin_password = admin + +# used for signing +;secret_key = SW2YcwTIb9zpOOhoPsMm + +# disable gravatar profile images +;disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +;data_source_proxy_whitelist = + +# disable protection against brute force login attempts +;disable_brute_force_login_protection = false + +# set to true if you host Grafana behind HTTPS. default is false. +;cookie_secure = false + +# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" +;cookie_samesite = lax + +# set to true if you want to allow browsers to render Grafana in a ,