# esay-do **Repository Path**: yuzhanfeng/easy-do ## Basic Information - **Project Name**: esay-do - **Description**: 学习和使用微服务项目的一个成果展示,准备做成一个开发脚手架、实现集成各种常用插件并且可插拔,少依赖,组装灵活、其他功能及核心代码以后陆续集成。 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://www.gebilaoyu.club - **GVP Project**: No ## Statistics - **Stars**: 6 - **Forks**: 0 - **Created**: 2021-03-14 - **Last Updated**: 2024-04-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # easy-do #### 介绍 使用微服务项目的成果积累,开发脚手架、集成各种常用插件,可插拔,少依赖,组装灵活、其他功能及核心代码以后陆续实现。 目前绝大部分涉及配置集成类的模块全部做成了starter,基本每个模块都可以单独打包进入新项目,做到开箱即用! #### 核心理念 插件! 插件! 插件! 核心理念就是各种常用功能全部做成插件 项目临时遇到需要的单独功能搭建一遍一遍百度很烦?从其他项目拷贝再修改很重复?那就用easydo吧! 你需要做的只有一件事,那就是引入依赖。 系统演示地址:http://easydo.plus 只是一个简单的前端展示。 ###本人词穷,这事当前已实现的比较完善的功能: ###### 1.代码生成 -再 ruoyi 的gen基础上增加动态数据源、在线模板管理 https://gitee.com/yuzhanfeng/easy-do-generate-standalone ###### 2.oauth2授权授权服务组件(扩展token用户信息,一系列自定义配置) ###### 3.oauth2资源服务服务组件 对请求鉴权、自动请求授权服务鉴权、细粒度权限控制注解 、鉴权工具类 ###### 4.oauth2客户端组件 自动请求授权服务获取token,微服务调用自动获取token ###### 5.redis使用组件的简单封装,fastjson序列化存储 ###### 6.jdbc动态数据源,封装了数据源管理api ###### 7.jpa组件,快速开发的基类 ###### 8.mybatis and mybatis plus 封装为插件,自动装配,快速开发的基类 ###### 9.文件存储服务,基于minio 抽象了统一操作接口,后续支持扩展其他文件服务支持 ###### 10.接口文档组件 kni4j的封装,采用稳定版本,无需代码配置,直接引入依赖即可使用 ###### 11.xxl-job客户端,支持注解配置自动注册任务、兼容最新版 ###### 12.全线基于springboot cloud Alibaba nacos 快速构建微服务模块 ```xml easy-do-core //核心基础模块 easy-do-utils //工具 easy-do-log //日志记录 easy-do-cloud-core //微服务web应用相关基础模块 easy-do-web-nacos //nacos web相关自动装配模块 easy-do-jpa-starter //JPA组件 easy-do-redis-starter //redis组件 easy-do-mybatis-starter //mybatis组件 easy-do-mybatis //mybatis-plus主键 easy-do-knife4j //接口文档、在线调试接口、一键下载生成接口文档 easy-do-oauth2-core //oauth2鉴权服务的基础模块 easy-do-oauth2-api //oauth2-api模块 easy-do-oauth2-server // 授权服务组件 easy-do-oauth2-client // 客户端组件 easy-do-oauth2-resources // 资源服务主键 easy-do-xxl-job // xxl-job分布式调度支持、自动注册实现 easy-do-jdbc-dynamic-datasource-starter //基于spring-jdbc实现的动态数据源,具有管理接口 easy-do-generate ///快速代码生成插件 网页操作 在线管理模板 easy-do-file-store-stater //文件存储插件 目前支持minio上传、下载、以及文件资源管理接口 ``` ###2021.11.1: ###easy-do-xxl-job 特性 - 支持xxl-job-admin 1.3.0, 1.2.0版本未测试,理论上兼容 - 事件自动注册执行器、自动注册调度任务、细粒度配置任务参数 ```yaml easydo.plus easy-do-xxl-job 1.0.1 ``` ```yaml xxl: job: admin: ### 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; addresses: http://127.0.0.1:8080/xxl-job-admin username: admin password: 123456 ### 执行器通讯TOKEN [选填]:非空时启用; accessToken: executor: ### 自动注册的执行器名称 title: job-center ### 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 appname: job-center ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; address: ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题 ip: ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; port: 0 ### 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; logpath: ### 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能 logretentiondays: -1 ``` 使用示例 ```java /** * 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 final Logger logger = LoggerFactory.getLogger(SampleXxlJob.class); /** * 1、简单任务示例(Bean模式) */ @XxlJob("demoJobHandler") @AutoRegister(scheduleConf = "0 * 0/1 * * ?", desc = "简单任务示例(Bean模式)", author = "easy-do") public void demoJobHandler() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World."); } /** * 2、分片广播任务 */ @XxlJob("shardingJobHandler") @AutoRegister(scheduleConf = "0 * 0/1 * * ?", desc = "分片广播任务", author = "easy-do") public void shardingJobHandler() throws Exception { } /** * 3、命令行任务 */ @XxlJob("commandJobHandler") @AutoRegister(glueType = GlueTypeEnum.GLUE_POWERSHELL,scheduleConf = "0 * 0/1 * * ?", desc = "命令行任务", author = "easy-do") public void commandJobHandler() throws Exception { } /** * 4、跨平台Http任务 * 参数示例: * "url: http://www.baidu.com\n" + * "method: get\n" + * "data: content\n"; */ @XxlJob("httpJobHandler") @AutoRegister(scheduleConf = "0 * 0/1 * * ?", desc = "跨平台Http任务", author = "easy-do") public void httpJobHandler() throws Exception { } /** * 5、生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑; */ @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") @AutoRegister(scheduleConf = "0 * 0/1 * * ?", desc = "生命周期任务示例:任务初始化与销毁时,支持自定义相关逻辑", author = "easy-do") public void demoJobHandler2() throws Exception { XxlJobHelper.log("XXL-JOB, Hello World."); } public void init(){ logger.info("init"); } public void destroy(){ logger.info("destory"); } } ``` AutoRegister注解说明 ```java /** * @author RaoYuan */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) @Documented public @interface AutoRegister { /**r任务详细*/ String desc(); /**调度配置参数*/ String scheduleConf() default ""; /**调度类型*/ ScheduleTypeEnum scheduleType() default ScheduleTypeEnum.CRON; /**负责人*/ String author(); /**执行器路由策略*/ RouteStrategyEnum executorRouteStrategy() default RouteStrategyEnum.FIRST; /**任务ID*/ String childJobId() default ""; /**阻塞处理策略*/ BlockStrategy executorBlockStrategy() default BlockStrategy.SERIAL_EXECUTION; /**运行模式*/ GlueTypeEnum glueType() default GlueTypeEnum.BEAN; /**超时时间*/ int executorTimeout() default 0; /**失败重试次数*/ int executorFailRetryCount() default 0; /**任务参数*/ String executorParam() default ""; /**GLUE备注*/ String glueRemark() default "GLUE代码初始化"; /**GLUE源码(命令行、java等调度方式使用)*/ String glueSource() default ""; /**报警接收的邮箱*/ String alarmEmail() default ""; /**调度过期策略*/ MisfireStrategyEnum misfireStrategy() default MisfireStrategyEnum.DO_NOTHING; } ``` ------------------------------ easy-do-xxl-job 一、 **Oauth2鉴权模块** 鉴权服务、资源服务、客户端服务分别为两个starter模块 ,按照服务需求引入依赖即可自动装配 鉴权服务(easy-do-oauth2-server): ``` easydo.plus easy-do-oauth2-server 1.0.1 ``` - 实现tokenInfo(oauth/token)返回用户数据的强扩展(用户信息、全部鉴权信息 配置文件可控制是否开启) - 实现openfeign微服务调用token传递、支持以client方式获取token调用其他资源服务, - 数据库持久化管理客户端信息、 - 支持配置文件设置自定义登录、授权页面、 - Redis存储token 序列化(默认关闭fastjson序列化,配置文件控制开关), 配置文件详解 ```yaml ################OAuth授权相关配置####################### security: oauth2: whiteList: ${WHITELIST:/actuator/**,/doc.html**,/swagger-resources,/v2/api-docs} client: oauth-server-name: ${OAUTH_SERVER_NAME:oauth-server} oauth-server-uri: ${OAUTH_SERVER:http://localhost} client-id: ${CLIENT_ID:oauth-server} client-secret: ${CLIENT_SECRET:oauth-server-secret} grant-type: ${GRANT_TYPE:client_credentials} access-token-uri: ${OAUTH_SERVER:http://localhost}/oauth/token} customize: access-token-validity-seconds: ${ACCESS_TOKEN_VALIDITY_SECONDS:72000} #token有效时间 refresh-token-validity-seconds: ${REFRESH_TOKEN_VALIDITY_SECONDS:259200} #刷新token有效时间 enable-fastJson-serializer: ${ENABLE_FASTJSON_SERIALIZER:true} #是否开启fastjson序列化 oath-token-prefix: ${OATH_TOKEN_PREFIX:'OAUTH2_TOKEN:'} #redisToken前缀 oauth-code-prefix: ${OAUTH_CODE_PREFIX:'AuthorizationCode:'} #redis授权码前缀 code-client-prefix: ${CODE_CLIENT_PREFIX:'ClientToAuthCode:'} #redis授权码客户端id前缀 oauth-code-length: 12 #授权码长度 login-path: ${LOGIN_PATH:/login} #自定义登陆页面控制层请求路径 login-html: ${LOGIN_HTML:login} #登陆页面html文件名 login-submit-url-key: ${LOGIN_SUBMIT_URL_KEY:loginSubmitUrl} #登录页面提交url的Key thymeleaf页面取值 confirm-access-path: ${CONFIRM_ACCESS_PATH:/custom/confirm_access} #自定义授权跳转页面的控制层路径 confirm-access-html-prefix: ${CONFIRM_ACCESS_HTML_PREFIX:oauth} #自定义授权页面的html文件名前缀 token-info-all: ${TOKEN_INFO_ALL:false} #checkToken请求 是否返回全部Authentication信息 token-info-user: ${TOKEN_INFO_USER:true} #checkToken请求 是否返回登录用户的详细信息 ``` 二、 资源服务(easy-do-oauth2-resource-starter): ``` easydo.plus easy-do-oauth2-resource-starter 1.0.1 ``` 配置文件填写授权服务地址、 客户端id 、密钥 等信息 ```yaml security: oauth2: whiteList: ${WHITELIST:/actuator/**,/doc.html**,/swagger-resources,/v2/api-docs} resource: id: ${RESOURCE_ID:user-center} token-info-uri: ${OAUTH_SERVER:http://localhost}/oauth/check_token client: client-id: ${CLIENT_ID:user-center} client-secret: ${CLIENT_SECRET:user-center-secret} ``` 启动类开启自动机权限注解 ```java @EnableCustomizePreAuthorize @EasyDoEnableOpenFeignClients @SpringBootApplication public class UserCenterMybatisPlusApplication ``` 自定义权限注解使用 ```java //跳过授权,不需要登录、携带token、可匿名访问 @IgnoreThePermissions // 权限字符校验,系统管理中配置的角色菜单权限字段 @ApiOperation(value = "分页条件查询oauth2客户端信息列表") @CustomizePreAuthorize(hasPermission = {"usercenter:clentDetails:page"}) @PostMapping("/page") public MPDataResult page(OauthClientDetailsQo qo) { IPage page = oauthClientDetailsService.page(qo); return ok(page); } //scope作用域校验 @PreAuthorize("hasScope('oauth-server')") @RequestMapping("/findByUserName") public R findByUserName(String userName) { } //注解内所有用法 /**具有权限字段则通过*/ String[] hasPermission() default {}; /**不具有指定权限字段则通过*/ String[] notHasPermission() default {}; /**具有特定scpo则通过*/ String[] hasScope() default {}; /**不具有指定scpo则通过*/ String[] notHasScope() default {}; /**指定的客户端才能访问*/ String hasClient() default ""; /**不是指定的客户端才能访问*/ String notHasClient() default ""; /**指定的资源服务才能访问*/ String[] hasResource() default {}; /**不是指定的资源服务才能访问*/ String[] notHasResource() default {}; /**scope具有写权限*/ boolean read() default false; /**scope具有读取权限*/ boolean write() default false; /**scope具有创建权限*/ boolean create() default false; /**scope具有删除权限*/ boolean delete() default false; /**scope具有ALL权限*/ boolean all() default false; ``` - 自定义Oauth2工具类 能够轻松获取用户信息和客户端信息。 - 自定义鉴权注解,实现细粒度权限控制,完美拓展原生的PreAuthorize注解 - 支持scope鉴权、角色鉴权、权限码鉴权、资源id鉴权、客户端id鉴权 admin用户自动放过 - 自定义注解,不需要写表达式,直接在注解设置相关属性即可(基于AOP Oauth2Utils 和自定义权限校验表达式)。 三、 客户端服务(easy-do-oauth2-resource-starter),直接引入: ``` easydo.plus easy-do-oauth2-client 1.0.1 ``` 配置文件填写授权服务地址、 客户端id 、密钥 等信息 ```yaml security: oauth2: client: oauth-server-name: ${OAUTH_SERVER_NAME:oauth-server} oauth-server-uri: ${OAUTH_SERVER:http://localhost} client-id: ${CLIENT_ID:oauth-server} client-secret: ${CLIENT_SECRET:oauth-server-secret} grant-type: ${GRANT_TYPE:client_credentials} access-token-uri: ${OAUTH_SERVER:http://localhost}/oauth/token} ``` ```java @OauthFeignClient(name = "user-center",path = "/user", fallbackFactory = UserServiceFallbackFactory.class) //注解的默认配置类支持token自动获取并传递 Class[] configuration() default {Oauth2FeignClientInterceptor.class}; ``` - openfeign调用自动获取token 、自动获取鉴权信息,保证通信安全性的同时更便捷。 四、 接口文档 (easy-do-knife4j): - 集成自knife4j 做了再次封装,直接引入,无需额外代码配置。 ``` easydo.plus easy-do-knife4j 1.0.1 ``` ```yaml knife4j: enable: true custom: basePackage: plus.easydo.server.user.mp.controller title: 接口文档标题 groupName: 分组名称 description: 接口文档描述 version: 1.0 prefix: / contact: name: easy-do url: www.easy-do.plus email: gebilaoyu@foxmail.com # documents: # - # group: 2.X版本 # name: 隔壁老于 # #文件夹 # locations: classpath:markdowns/* # #单个文件 # locations: classpath:md/sign.md setting: language: zh-CN enableSwaggerModels: true enableDocumentManage: true swaggerModelName: 实体类列表 enableVersion: false enableReloadCacheParameter: false enableAfterScript: true enableFilterMultipartApiMethodType: POST enableFilterMultipartApis: false enableRequestCache: true enableHost: false #enableHostText: 127.0.0.1:8000 enableHomeCustom: false homeCustomLocation: classpath:markdown/home.md enableSearch: true enableFooter: true enableFooterCustom: true footerCustomContent: Apache License 2.0 | Copyright 2021-[easy-do](https://gitee.com/yuzhanfeng/easy-do) enableDynamicParameter: true enableDebug: true enableOpenApi: true enableGroup: true cors: false production: false basic: enable: false username: admin password: admin ``` 五. mybais-plus (easy-do-mybatis) 集成动态数据源、无需额外配置,引入依赖即可 - 动态数据源集成自(项目地址:https://gitee.com/baomidou/dynamic-datasource-spring-boot-starter) 做了再次封装,直接引入,无需额外代码配置。 ``` easydo.plus easy-do-mybatis 1.0.1 ``` 提供的快速开发java类,以及使用示例 ```java //几个常用的类 plus.easydo.starter.mybatis.plus.base.BaseController plus.easydo.starter.mybatis.plus.entity.MPBaseEntity plus.easydo.starter.mybatis.plus.qo.MpBaseQo.clss plus.easydo.starter.mybatis.plus.result.MPDataResult plus.easydo.starter.mybatis.plus.result.MPResultUtil //使用示例 /** * 用户信息 * * @author ruoyi */ @RestController @RequestMapping("/user") public class SysUserController extends MPBaseController { @PreAuthorize("hasScope('oauth-server')") @RequestMapping("/findByUserName") public R findByUserName(String userName) { SysUser user = userService.selectUserByUserName(userName); return ok(user); } /** * 获取用户列表 */ @CustomizePreAuthorize(hasPermission = {"system:user:list"}) @PostMapping("/page") public MPDataResult page(@RequestBody UserQo qo) { IPage page = userService.page(qo); return ok(page); } /** * 新增用户 */ @PreAuthorize("hasPermission('system:user:add')") @PostMapping public R add(@Validated @RequestBody SysUser user) { if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(user.getUserName()))) { return fail("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); } else if (StringUtils.isNotEmpty(user.getPhoneNumber()) && UserConstants.NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) { return fail("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); } else if (StringUtils.isNotEmpty(user.getEmail()) && UserConstants.NOT_UNIQUE.equals(userService.checkEmailUnique(user))) { return fail("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); } user.setCreateBy(Oauth2Utils.getUserName()); user.setPassword(Oauth2Utils.encryptPassword(user.getPassword())); return opResult(userService.insertUser(user)); } /** * 修改用户 */ @PreAuthorize("hasPermission('system:user:edit')") @PutMapping public R edit(@Validated @RequestBody SysUser user) { return opResult(userService.updateUser(user)); } /** * 删除用户 */ @PreAuthorize("hasPermission('system:user:remove')") @DeleteMapping("/{userIds}") public R remove(@PathVariable Long[] userIds) { return opResult(userService.deleteUserByIds(userIds)); } } ``` ```yaml spring: # 数据源配置 datasource: druid: stat-view-servlet: enabled: true loginUsername: root loginPassword: root dynamic: primary: master #设置默认的数据源或者数据源组,默认值即为master lazy: false #默认立即初始化数据源,true则支持在需要建立连接时再初始化数据源 strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: master: url: jdbc:mysql://${MYSQL_HOST:XXXXXX}:${MYSQL_PORT:3306}/${MYSQL_DATA_BASE:easy-do}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: ${MYSQL_USER:easy-do} password: ${MYSQL_PASS:easy-do} driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置 # slave_1: # url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic # username: root # password: 123456 # driver-class-name: com.mysql.jdbc.Driver # slave_2: # url: ENC(xxxxx) # 内置加密,使用请查看详细文档 # username: ENC(xxxxx) # password: ENC(xxxxx) # driver-class-name: com.mysql.jdbc.Driver # schema: db/schema.sql # 配置则生效,自动初始化表结构 # data: db/data.sql # 配置则生效,自动初始化数据 # continue-on-error: true # 默认true,初始化失败是否继续 # separator: ";" # sql默认分号分隔符 # lazy: true #可独立配置是否启用懒启动 ``` 六.集成redis组件,自带redisServer ``` easydo.plus easy-do-redis-starter 1.0.1 ``` 4.nacos注册中心 5.gateway网关 6.admin监控、健康状态检查 #### 安装教程 1. 拉取源码 2. 本地环境需要 nacos 、mysql(暂时用于nacos持久化) 、Emqx(emqtt)、打包admin、gateway服务并启动 3. 搬砖 #### 使用说明 1. xxxx 2. xxxx 3. xxxx #### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request