概述
官网,中文名爱组搭,基于Spring Boot的开源低代码平台,灵活、智能、强大的应用可视化设计与发布平台,能够实现分钟级搭建表单、MIS、CRM、OA等智能化、免运维的各类企业SaaS系统与项目应用,助力企业降本增效,快速构建数据与应用中台实现业务智能化升级。提供安全、限流、监控、OSS 存储、机器人通知等多种可插拔组件,用于快速构建企业级应用。理念是“每个人都是架构师”,通过组件化方式提升开发效率。
开源组织
https://gitee.com/aizuda
FlowLong
另起一篇,参考企业级无代码工作流引擎FlowLong:简介、实战。
Snail-Job
另起一篇,参考Snail-Job:简介、原理、实战。
PanShi
Java+Vue+TypeScript实现、开源(Gitee,2.6K Star,1.2K Fork)稳定可靠的Web开发框架,官方文档。
| 后端技术栈 | 前端技术栈 |
|---|---|
| JDK 17+ | TypeScript |
| SpringBoot 3.X | Vue3.X |
| MybatisPlus 3.X | Vite |
| SpringDoc | ElementPlus |
| PostgreSQL、MySQL、Oracle… | QingAdmin |
实战
系统首页
MongoPlus
官网,开源(Gitee,1.4K Star,247 Fork;GitHub,75 Star,11 Fork)使用MyBatisPlus的方式,优雅的操作MongoDB,官方文档。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作
- 强大CRUD操作:通用Service,仅仅通过少量配置即可实现单表大部分CRUD操作,更有强大的条件构造器,满足各类使用需求
- 支持Lambda形式调用:通过Lambda表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达5种主键策略(内含分布式唯一ID生成器-Sequence),可自由配置,完美解决主键问题
- 支持自定义全局通用操作:支持全局通用方法注入
- 支持无实体类情况下的操作
- 支持动态数据源
- 支持逻辑删除、防止全集合更新和删除、自动填充、自动分页等等功能
原理
不出意外,采用Maven多模块设计思路
注解:
- @CollectionName:集合名注解,标识实体类对应的集合
- @ID:主键注解,会指向MongoDB中
_id字段
IdTypeEnum,主键类型可用枚举:
| 值 | 描述 |
|---|---|
| OBJECT_ID | 生成mongoDB自带的_id |
| ASSIGN_UUID | 生成UUID |
| ASSIGN_ULID | 生成ULID,效率比UUID高?? |
| ASSIGN_ID | 分配ID(主键类型为Number(Long和Integer)或String)(since3.3.0),使用接口IdentifierGenerator的方法nextId,默认实现类为DefaultIdentifierGenerator,雪花算法 |
- @CollectionField:字段注解
| 属性 | 类型 | 必须指定 | 默认值 | 描述 |
|---|---|---|---|---|
value | String | 否 | “” | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字 段 |
convertCollect | boolean | 否 | false | 如果值时对象,接收字段是集合,可以使用此配置,会将对象自动转为集合,只存在这一个值 |
fill | FieldFill | 否 | FieldFill.DEFAULT | 自动填充策略 |
typeHandler | Class<?> | 否 | Void.class | 类型处理器,必须实现com.mongoplus.handlers.TypeHandler接口 |
ignoreNull | boolean | 否 | true | 忽略null属性 |
isObjectId | boolean | 否 | false | 是否是ObjectId |
- @MongoTransactional:声明式事务,标注到方法上,表示该方法需要受到事务控制
- @CollectionLogic:逻辑删除,开启全局逻辑删除情况下,用于自定义该文档逻辑删除字段及忽略逻辑删除
| 属性 | 类型 | 必须指定 | 默认值 | 描述 |
|---|---|---|---|---|
value | String | 否 | “” | 默认逻辑未删除值(该值可无、会自动获取全局配置) |
delval | String | 否 | “” | 默认逻辑未删除值(该值可无、会自动获取全局配置) |
delType | LogicDataType | 否 | DEFAULT | 逻辑删除字段值类型 |
- @IgnoreTenant:忽略多租户注解,可标注在方法上,优先级高
- @MongoDs:多数据源注解
| 属性 | 类型 | 必须指定 | 默认值 | 描述 |
|---|---|---|---|---|
value | String | 是 | “” | 数据源名称 |
dsHandler | Class<?> | 否 | Void.class | 数据源处理类,需实现com.mongoplus.handlers.DataSourceHandler接口 |
提供通用Service CRUD封装泛型接口IService,进一步封装CRUD,泛型T为任意实体对象。
基础拦截器接口com.mongoplus.interceptor.Interceptor,所有拦截器都应实现此接口,选择性的实现接口中的方法:
executeSave(documentList):添加拦截器方法executeRemove(filter):删除拦截器方法executeUpdate(queryBasic,updateBasic):更新拦截器方法executeQuery(queryBasic, projectionList, sortCond):查询拦截器方法executeAggregate(aggregateConditionList):管道拦截器方法executeCount(queryBasic,countOptions):统计拦截器方法executeBulkWrite(writeModelList):批量操作拦截器方法executeSave(documentList, collection):添加拦截器方法executeRemove(filter, collection):删除拦截器方法executeUpdate(queryBasic,updateBasic, collection):更新拦截器方法executeQuery(queryBasic, projectionList, sortCond, collection):查询拦截器方法executeAggregate(aggregateConditionList, collection):管道拦截器方法executeCount(queryBasic,countOptions, collection):统计拦截器方法executeBulkWrite(writeModelList, collection):批量操作拦截器方法
高级拦截器com.mongoplus.interceptor.AdvancedInterceptor,提供权限更高的操作,且可直接返回数据,类似MyBatis拦截器,会根据所有注册的高级拦截器和普通拦截器,形成拦截器责任链,高级拦截器更靠近底层。
TenantHandler:Mongo-Plus提供的多租户处理器插件,用于实现多租户的数据隔离,可确保每个租户只能访问自己的数据,实现数据的安全隔离。方法如下:
BsonValue getTenantId():获取租户idString getTenantIdColumn():获取租户字段,默认tenant_idboolean ignoreCollection(collectionName):根据集合名判断是否忽略租户boolean ignoreDatabase(database):根据数据库名判断是否忽略租户boolean ignoreDataSource(dataSource):根据数据源名判断是否忽略租户boolean ignoreInsert(columns, tenantIdColumn):忽略插入租户字段逻辑
CollectionNameHandler:动态集合处理器,可用于根据不同的条件查询不同的集合的场景。允许在运行时动态地改变MongoDB语句中的集合名,这对于处理分表逻辑非常有用。提供方法String dynamicCollectionName(ExecuteMethodEnum,Object[],MongoNamespace),参数分别对应:执行的方法枚举、参数、命名空间。
实现CollectionNameHandler接口,创建动态集合处理器,并注册为Bean。如果查询参数中包含userName参数,且userName参数值为张三,则将集合名称修改为user_zhangsan:
@ComponentpublicclassCustomCollectionNameHandlerimplementsCollectionNameHandler{@OverridepublicStringdynamicCollectionName(ExecuteMethodEnumexecuteMethodEnum,Object[]source,MongoNamespacenamespace){if(executeMethodEnum==ExecuteMethodEnum.QUERY){// 具体类型参数转换参考com.mongoplus.execute.Execute接口,// 比如查询(executeQuery(Bson, BasicDBObject, BasicDBObject, Class<T>, MongoCollection<Document>)),// 对应的第0个参数类型为BsonBsonbson=(Bson)source[0];BsonStringuserName=bson.toBsonDocument().getString("userName",newBsonString(""));if(userName.getValue().equals("张三")){return"user_zhangsan";}}//条件不满足则返回原名称returnnamespace.getCollectionName();}}数据变动记录拦截器:DataChangeRecorderInnerInterceptor,不仅能够自动记录操作日志、添加到数据库,还支持安全阈值控制,如限制批量更新或插入的数量。方法列表:
setExceptionMessage(String):超出阈值提示信息setIgnoredColumnList(List):忽略的表setBatchUpdateLimit(Integer):批量更新条数上限setdisplayCompleteData(Boolean):是否显示完整数据,开启后,changedData字段数据量可能会很大enableSaveDatabase(BaseMapper):开启将记录信息保存到数据库setDatasourceName(String):数据源,默认获取上下文中的数据源isMasterDatasource(Boolean):是否使用默认数据源保存数据,如果设置datasourceName,该配置会失效,但切换数据源并不会失效,推荐设置,使用主数据源,避免连续切库setDatabaseName(String):数据库,默认获取上下文中对应的数据源的库setCollectionName(String):集合名,默认为DATA_CHANGE_RECORD
乐观锁是一种并发控制机制,用于确保在更新记录时,该记录未被其他事务修改。OptimisticLockerInterceptor插件,使得在应用中实现乐观锁变得简单。
实现步骤:
- 读取记录时,获取当前的版本号version
- 在更新记录时,将这个版本号一同传递
- 执行更新操作时,设置
version = newVersion的条件为version = oldVersion - 如果版本号不匹配,则更新失败
配置插件
@BeanpublicOptimisticLockerInterceptoroptimisticLockerInterceptor(){OptimisticLockerInterceptorinterceptor=newOptimisticLockerInterceptor();// 修改失败后是否抛出异常抛出异常interceptor.setUpdateFailException(newRuntimeException("修改失败"));// 未获取到乐观锁字段是否抛出异常interceptor.setVersionIsNullException(newMongoPlusException("未获取到乐观锁字段"));// 设置乐观锁自增值interceptor.setAutoInc(1);// 是否开启重试interceptor.enableRetry(getRetryStrategy());returninterceptor;}/** * 获取重试策略 */RetrygetRetryStrategy(){returnRetry.builder()// 最大重试次数.maxRetryNum(10)// 首次命中回调.hitRetry(updateRetryResult->log.info("首次命中: "+updateRetryResult))// 修改成功回调.onSuccess(object->log.info("修改成功: "+object))// 修改失败回调.onFailure(object->log.info("修改失败: "+object))// 达到最大重试次数失败后的备用逻辑.fallback(((updateRetryResult,invocation)->{try{returninvocation.proceed();}catch(Throwablee){thrownewRuntimeException(e);}}))// 每次重试增加version值.autoVersionNum(1)// 重试是否继续执行后续拦截器,默认为true// 如果后续的执行器中,进行了某些操作,请将该属性设置为false。比如在后续拦截器中做了修改其他表的操作,在重试时则会修改多次.processIntercept(true)// 重试间隔时间.retryInterval(100L)// 开启异步重试,会立即返回当前更新失败的响应,并且异步的重试,不受事务控制。可以通过回调配置查看最终结果.asyncRetry(true).build();}在实体类中,需要在表示版本号的字段上添加@Version注解:
importcom.mongoplus.annotation.collection.Version;publicclassUser{@VersionprivateIntegerversion;}实战
Maven项目引入依赖:
<dependencyManagement><dependencies><!-- mongoplus-bom --><dependency><groupId>com.mongoplus</groupId><artifactId>mongo-plus-bom</artifactId><version>2.1.9</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><!-- springboot --><dependency><groupId>com.mongoplus</groupId><artifactId>mongo-plus-boot-starter</artifactId></dependency><!-- solon生态 --><dependency><groupId>com.mongoplus</groupId><artifactId>mongo-plus-solon-plugin</artifactId></dependency><!-- springboot4.X --><dependency><groupId>com.mongoplus</groupId><artifactId>mongo-plus-boot4-starter</artifactId><!-- 需指定版本号 --><version>2.1.9</version></dependency>在application.yml配置文件中添加MongoPlus相关配置:
mongo-plus:data:mongodb:host:127.0.0.1port:27017database:testusername:test# 用户名,没有可不填;若账号中出现@,!等符号,无需转码password:root#密码,同上authenticationDatabase:admin# 验证数据库connectTimeoutMS:50000# 在超时之前等待连接打开的最长时间,以毫秒为单位QingAdmin
基于VUE 3.0、开源(Gitee,175 Star,37 Fork)简单易用轻后台管理框架,
技术栈:Vue 3、TypeScript、Vite、Element Plus
实战
基于源码部署体验:
gitclone https://gitee.com/aizuda/qing-admin.gitcdqing-adminnpminstallEasy-Security
官网,开源(Gitee,108 Star,16 Fork)、纯Java实现、基于过过滤器实现的简单认证鉴权框架,核心只关注如何获取用户和校验,以及如何获取权限和校验,提供自定义插件,轻松扩展业务实现。官方文档。
版本依赖关系
| java | spring | easy-security | 备注 |
|---|---|---|---|
| java8 | 2.7.x | 2.1.1 | 已支持 |
| java17 | 3.x | 3.1.2 | 已支持 |
实战
Maven项目引入如下依赖:
<dependency><groupId>com.aizuda</groupId><artifactId>easy-security-boot-starter</artifactId></dependency>