# service-abc **Repository Path**: linlurui/service-abc ## Basic Information - **Project Name**: service-abc - **Description**: 基于spring-cloud3的微服务领域驱动开发框架 - **Primary Language**: Java - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2022-05-20 - **Last Updated**: 2024-08-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # service-abc开发框架说明 * 基于spring-cloud3采用领域驱动开理念搭建的微服务快速开发框架。 * abc开发理念即api-business-clients,api提供接口服务传递基础数据到business处理业务逻辑,clients暴露接口相互调用型在闭环,abc三者之间引用关系形成良性循环。 # ABC设计理念 ![post](https://gitee.com/linlurui/service-abc/raw/master/images/design.png) # ABC微服务架构 ![post](https://gitee.com/linlurui/service-abc/raw/master/images/topic.png) # 创建项目步骤 ## 步骤一:新建项目 ![post](https://gitee.com/linlurui/service-abc/raw/master/images/step1/new1.jpg) ![post](https://gitee.com/linlurui/service-abc/raw/master/images/step1/new2.jpg) ## 步骤二:Maven配置 * 点开修改pom.xml ![post](https://gitee.com/linlurui/service-abc/raw/master/images/step2/step2.jpg) ### 1) 配置maven包下载仓库 ```xml true always true always ignore abc-dev https://gitee.com/linlurui/service-abc/raw/release ``` ### 2) 配置maven插件仓库 (和repository配置成一样的) ```xml true always true always ignore abc-dev https://gitee.com/linlurui/service-abc/raw/release ``` ### 3) 添加maven插件配置 ```xml abc-dev abc-generator 0.0.1-SNAPSHOT ${project.name} /api true id,createdOn tenant,user deleted lock_version mp HashMap false service-abc clean abc-generate ``` ### 4) 项止主pom添加packaging ```xml pom ``` ## 步骤三:配置数据源 * 首次生成项目根目录下需新增application.yml配置如下数据源,生成目录结构后数据源配置会同步生成到abc-api目录下的application.yml,再次生成只需维护abc-api目录下的application配置项 ![post](https://gitee.com/linlurui/service-abc/raw/master/images/step4/application.jpg) ```yaml spring: datasource: # 动态数据源 dynamic: primary: auth #设置默认的数据源或者数据源组,默认值即为master strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源 datasource: # 多数据源属性 master: url: jdbc:mysql://localhost:3306/master username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver auth: url: jdbc:mysql://localhost:3306/auth username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` ## 步骤四:刷新Maven并执行Clean ![post](https://gitee.com/linlurui/service-abc/raw/master/images/step3/clean.jpg) * 初始化数据库的脚本可以放在resources/sql/init.sql文件中, 如: ```sql Create Database If Not Exists `auth` Character Set utf8mb4; Create Table If Not Exists `auth`.`online` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '表id', `account` varchar(16) NOT NULL COMMENT '登录账号', `platform` varchar(32) NOT NULL COMMENT '登录平台', `token` varchar(2048) DEFAULT NULL COMMENT '当前Token', `login_time` datetime DEFAULT NULL COMMENT '登录时间', `account_state` int DEFAULT 0 COMMENT '账号状态', `ip` varchar(16) DEFAULT NULL COMMENT '登录IP', `expir` bigint DEFAULT NULL COMMENT '过期时间', `private_key` varchar(64) DEFAULT NULL COMMENT '私钥', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=14503 DEFAULT CHARSET=utf8mb4 COMMENT='在线账号'; ``` ## 步骤五:启动项目 * 假设${project.name}为dev,展开abc-api下的abc.dev目录,运行ServiceAbcApplication ![post](https://gitee.com/linlurui/service-abc/raw/master/images/step5/run.jpg) # 项目目录结构 ![post](https://gitee.com/linlurui/service-abc/raw/master/images/project.jpg) * abc-api/controller (接口服务控制器) * abc-api/service (数据库访问层) * abc-api/service/impl (数据访问实现层) * abc-api/mapper (数据访问映射层) * abc-api/mapper/xml (数据访问映射实现,重新生成会被覆盖,增强需求需要用继承实现) * abc-clients/to (数据传输实体,重新生成会被覆盖) * abc-clients/vo (数据返回实体,默认继承to) * abc-clients/client (feign客户端,重新生成会被覆盖) * abc-business/bll (业务处理层) * abc-business/model (数据访问模型,可转vo,重新生成会被覆盖) # 引用core包 ```xml abc-dev abc-core 0.0.1-SNAPSHOT ``` # 框架统一预留注解 ```java @Permission //权限认证, 应用范围:类、方法 @Pass //跳过认证, 应用范围:方法 @ServiceAccess //服务访问鉴权, 应用范围:类、方法, AccessState默认为DECLINED @Associative //联动字段,abc.api.associative-query-enable=true时从该注解获取联动查询列表,联动更新为abc.api.associative-update-enable @PostAction //BaseController查询数据后传入BLL的方法 @PreAction //BaseController更新数据前传入BLL的方法 @GetByIdAction //根据ID获取数据, 应用范围:方法 @QueryPageAction //查询分页数据, 应用范围:方法 @QueryAction //查询数据, 应用范围:方法 @ExistAction //查询是否存在数据, 应用范围:方法 @CountAction //查询统计数据, 应用范围:方法 @InsertAction //新增数据 (可批量), 应用范围:方法 @UpdateAction //修改数据, 应用范围:方法 @DeleteAction //删除数据, 应用范围:方法 @DeleteBatchAction //批量删除数据, 应用范围:方法 @MinAction //查询指定字段最小值, 应用范围:方法 @MaxAction //查询指定字段最大值, 应用范围:方法 @SumAction //查询指定字段之和, 应用范围:方法 @AvgAction //查询指定字段平均值, 应用范围:方法 @UploadAction //上传文件, 应用范围:方法 @DownloadAction //下载文件, 应用范围:方法 @PlayVideoAction //播放视频, 应用范围:方法 @ExportAction //导出数据, 应用范围:方法 ``` # application.yml (ABC配置说明) ```yaml abc: md5: publicKey: abc # MD5加密公钥 api: # API相关配置 associative-query-enable: true # 是否开启联动查询,若开启框架执行DataExecuor的toVo()时会查询@Associative指定的子表数据 associative-update-enable: true # 是否开启联动更新,若开启框架执行DataExecuor的更新方法时会提交数据@Associative指定的子表,外键由Associative注解指定 keyword-search-enable: true # 是否开启关键词查询功能,若开启则支持分隔符处理OR条件,分隔符默认为空格 keyword-splitter: ; # 关键词查询分隔符,默认为空格 keyword-search-el-expression-open: true # 是否开启关键词查询EL表达式,若开启则DataExecuor查询数据时支持字段值填写EL表达式, validation-enable: true # 是否开启校验器 #校验器配置规则 validation: - name: mobile #提交参数名 pattern: 1[3578][0-9]{9} #匹配正则表达式 message: 请输入正确手机号 #未匹配的提示消息 require: true #是否必填 methods: post,put #请求方式,多请求方式以逗号分隔 ``` # El表达式查询条件 ### 默认查询接口支持El表达式,表达式写法如下所示: * IN查询: ${1,2,3,4,5,...} ```json { "id: "${1,2,3,4,5}" } ``` * 数字范围值查询: ${1-3,5-7,...} ```json { "id: "${1-3,5-7}" } ``` * 时间范围值查询: ${2022/01/01 00:00:00 - 2022/01/01 01:00:00,...} 注意:时间格式为 yyyy/MM/dd HH:mm:ss-yyyy/MM/dd HH:mm:ss 或 yyyy-MM-dd HH:mm:ss~yyyy-MM-dd HH:mm:ss ```json { "create_time: "${2022/01/01 00:00:00 - 2022/01/01 01:00:00}" } ``` 或 ```json { "create_time: "${2022-01-01 00:00:00 ~ 2022-01-01 01:00:00}" } ``` * 高级查询: 高级查询为特殊查询字段,字段名必须为$ADVANCE_SEARCH,支持多字段如:$ADVANCE_SEARCH、$ADVANCE_SEARCH_1、$ADVANCE_SEARCH_2,以下划线加下标表示,以此类推;多字段间表达式将转换成AND条件查询,表达式如:${name like 字典,component=zjlj},表达式之间以逗号分隔并将转换为OR条件,表达式操作符支持>、=、<、>=、<=、 like ```json { "$ADVANCE_SEARCH": "${name like 字典,component=zjlj}", "$ADVANCE_SEARCH_1": "${name like 字典,component=kk}" } ``` # SpringDoc文档地址 ### http://{domain}:{port}/doc.html * 例:http://localhost:8080/doc.html # Swagger文档地址 ### http://{domain}:{port}/swagger-ui.html * 例:http://localhost:8080/swagger-ui.html # 框架默认接口 (BaseController) ### 接口URL请求规则 * URL:http://{domain}:{port}{context}/{datasource}/{project.name}/{table}/{id} * 请求方式:GET、POST、PUT、DELETE * URL参数:{table}为数据库表名称,{id}为主键 * 示例:http://127.0.0.1:8088/api/dev/user/1 ### URL参数说明 * 注意:表名采用下划线连接的在URL参数中应去掉下划线作为table参数,如:user_info->userinfo ```yaml domain: 域名或IP port: 端口 context: 前辍,如:/api project.name: 生成代码时填入的项目名称 datasource: 数据源名称 table: 表名 id: 主键 field: 字段名 index: 文件索引, 如上传多张图片到某个字段中, 取第0个文件 ``` ### 返回参数说明 ```javascript { "status": 0, //状态码 "message": "OK", //消息 "data": { //返回数据 "1573228912416047106": true }, "pageInfo": null, //分页对象 "uuid": null //当前请求UUID } ``` ### 默认接口清单: 注:默认接口清单中的URL一律以相对路径表示,实际请求时请自行拼接上http://{domain}:{port}{context}/{datasource}/{project.name} #### 1. 新增数据 (可批量) * URL:/{table}/insert * 请求方式:PUT * 提交参数: ```json [ { "{field}": "值", ... } ... ] ``` * 返回: ```json { "status": 0, "message": "OK", "data": { "1573228912416047106": true }, "pageInfo": null, "uuid": null } ``` #### 2. 删除数据 * URL:/{table}/{id} * 请求方式:DELETE #### 3. 批量删除数据 * URL:/{table}/delete * 请求方式:DELETE * 提交参数({id}数组): ```json [ 1, 2, 3, ... ] ``` #### 4. 修改数据 * URL:/{table}/{id} * 请求方式:PUT * 提交参数: ```json { "${field}": "值", ... } ``` #### 5. 查询数据 * URL:/{table}/query * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` * 排序查询参数$ORDERS,该字段是特殊查询字段,用于查询排序 ```json { "$ORDERS": "[{\"column\":\"id\", \"asc\":\"false\"}]" } ``` #### 6. 分页查询 * URL:/{table}/query/{pageIndex}/{pageSize} * pageIndex: 当前页码 * pageSize:每页显示多少条 * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` * 排序查询参数$ORDERS,该字段是特殊查询字段,用于查询排序 ```json { "$ORDERS": "[{\"column\":\"id\", \"asc\":\"false\"}]" } ``` #### 7. 查询总数 * URL:/{table}/count * 请求方式:POST * URL参数:{table}为数据库表名称 * 查询参数: ```json { "${field}": "值", ... } ``` #### 8. 查询是否存在数据 * URL:/{table}/exist * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` #### 9. 根据ID获取数据 * URL:/{table}/{id} * 请求方式:GET #### 10. 下载文件 * URL:/{datasource}/download/{table}/download/{field}/{id}/{index} * 请求方式:GET #### 11. 上传文件 * URL:/{table}/upload 或 * URL:/{table}/{field}/upload/{id} * 请求方式:POST * 表单参数: ```text 表单: file: 文件1 file: 文件2 file: 文件3 ... ``` #### 12. 视频播放 * URL:/{table}/play/{field}/{id}/{index} * 请求方式:GET #### 13. 导出Excel * URL:/{table}/export * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` #### 14. 导入Excel * URL:/{table}/import * 请求方式:POST * 表单参数: ```text 表单: file: 文件1 file: 文件2 file: 文件3 ``` * Excel文件格式: 1. 需要导入的excel文件中新增一个名称为schema的sheet 2. schema的第一行为需要导入的表格表头 3. schema的第二行为对应数据库的字段名 #### 15. 统计字段最小值 * URL:/{table}/min/{field} * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` #### 16. 统计字段最大值 * URL:/{table}/max/{field} * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` #### 17. 统计字段之和 * URL:/{table}/sum/{field} * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` #### 18. 统计字段平均值 * URL:/{table}/avg/{field} * 请求方式:POST * 查询参数: ```json { "${field}": "值", ... } ``` #### 19. 插入表单数据(支持上传文件) * URL:/{table}/form/insert * 请求方式:POST * 表单参数: ```text 表单: 字段1: 文件|字符串 字段2: 文件|字符串 字段3: 文件|字符串 ``` # 自定义ISqlInjector * 可以通过实现ISqlInjector注册到springboot的bean工厂实现自定义的mybatis-plus默认方法 # 自定义sql拦截器 * 可以通过实现InnerInterceptor注册到springboot自定义的sql拦截器 ```java @Component @Qualifier("sqlInterceptor") public class SqlInterceptor implements InnerInterceptor { @Override public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { //step1. 查询是否存在需要构建高级查询的参数 ,如果没有就直接返回 QueryWrapper queryWrapper = (QueryWrapper)((Map)parameter).get("ew"); // 没有参数直接返回 if (null == queryWrapper) { return; } //step2. 获取到当前需要执行的sql String buildSql = boundSql.getSql(); //step3. 拼接查询条件 buildSql = String.format("select temp.* from ( %s ) as temp where %s ", buildSql, "1=1"); //step4. 修改完成的sql 再设置回去 PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql); mpBoundSql.sql(buildSql); } } ``` * 也可以通过实现并注入ISqlInterceptorFactory工厂类返回注册多个sql拦截器 ```java @Primary @Component @Qualifier("sqlInterceptorFactory") public class QueryInterceptorFactory implements ISqlInterceptorFactory { @Override public List getInterceptors() { return new ArrayList<>() {{ add(new QueryInterceptor()); }}; } @Override public void add(T interceptor) { } } ``` # 上传下载、导入、导出自定义响应 * IUploadHandler,重写该接口并注册Bean可在默认上传接口自定义上传下载实现 #### 示例 ```java @Component public class MyUpload implements IUploadHandler { /*** * 上传文件 * @param basePath 相对路径 * @param filename 文件名 * @param data 文件流 * @return * @throws Exception */ @Override public String upload(String basePath, String filename, byte[] data) { return null; } /*** * 下载文件 * @param uploadPath 文件上传路径 * @return 文件流 * @throws Exception */ @Override public byte[] download(String uploadPath) { return new byte[0]; } } ``` * IDataImportHandler,重写该接口并注册Bean可在默认导入接口接收文件流自定义导入数据 #### 示例 ```java @Component public class MyDataImport implements IDataImportHandler { /*** * 导入excel数据 * @param dataBuffer excel文件数据流 * @return * @throws Exception */ @Override public void call(String dbname, String table, T model, DataBuffer dataBuffer) { } } ``` * IDataImportHandler,重写该接口并注册Bean可在默认导入接口接收文件流自定义导入数据 #### 示例 ```java @Component public class MyDataExport implements IDataExportHandler { /*** * 导出数据 * @param modelList model列表 * @return * @throws Exception */ @Override byte[] call(List modelList); } } ``` # 自定义SqlHelper * 可以通过自定义SqlHelper来兼容多种类数据库,需实现ISqlHelper接口的getTableList、getColumnList两个方法,Qualifier可指定的值为:MysqlHelper、OracleHelper、SqLiteHelper、SqlServerHelper、PostgresqlHelper、DB2Helper、SybaseHelper ```java @Component @Qualifier("sqlServerHelper") public class DefaultSqlHelper implements ISqlHelper{ @Override public List> getTableList(String dbname, JdbcTemplate jdbcTemplate) { return null; } @Override public List> getColumnList(String dbname, JdbcTemplate jdbcTemplate) { return null; } } ``` * getTableList需返回所有表名,需包含栏位:table_name、type、table_comment * getColumnList需返回所有表的字段信息,需包含栏位:table、name、annotationColumnName、propertyName、comment、dataType、isAutoIncrement、keyFlag、keyIdentityFlag、convert、versionField # 注意事项 * abc-clients/to、abc-clients/client、abc-business/model这几个包与数据库字段息息相关,应与数据库的数据结构保持一致,所以重新clean生成代码时会被覆盖。