diff --git a/opsli-api/src/main/java/org/opsli/api/base/result/ResultVo.java b/opsli-api/src/main/java/org/opsli/api/base/result/ResultVo.java index a218e4f7..a54d83ed 100644 --- a/opsli-api/src/main/java/org/opsli/api/base/result/ResultVo.java +++ b/opsli-api/src/main/java/org/opsli/api/base/result/ResultVo.java @@ -25,7 +25,7 @@ import java.io.Serializable; */ @Data @ApiModel(value="视图层返回Api对象", - description="视图层返回Api对象 success:成功状态 code:编号 msg:信息 datatime:时间戳") + description="视图层返回Api对象 success:成功状态 code:编号 msg:信息 timestamp:时间戳 data:数据") public class ResultVo implements Serializable { private static final long serialVersionUID = 1L; diff --git a/opsli-api/src/main/java/org/opsli/api/web/test/TestApi.java b/opsli-api/src/main/java/org/opsli/api/web/test/TestApi.java index 97ccd60f..4acf359f 100644 --- a/opsli-api/src/main/java/org/opsli/api/web/test/TestApi.java +++ b/opsli-api/src/main/java/org/opsli/api/web/test/TestApi.java @@ -4,6 +4,10 @@ import io.swagger.annotations.ApiOperation; import org.opsli.api.base.result.ResultVo; import org.opsli.api.wrapper.test.TestModel; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; /** @@ -23,7 +27,6 @@ import org.springframework.web.bind.annotation.GetMapping; public interface TestApi { - @ApiOperation(value = "发送邮件", notes = "发送邮件") @GetMapping("/sendMail") ResultVo sendMail(); @@ -32,7 +35,6 @@ public interface TestApi { * 发送 Redis 订阅消息 * @return */ - @ApiOperation(value = "发送 Redis 订阅消息", notes = "发送 Redis 订阅消息") @GetMapping("/sendMsg") ResultVo sendMsg(); @@ -41,7 +43,6 @@ public interface TestApi { * 发送 Redis 订阅消息 * @return */ - @ApiOperation(value = "发送 Redis 测试", notes = "发送 Redis 测试") @GetMapping("/redisTest") ResultVo redisTest(); @@ -50,7 +51,6 @@ public interface TestApi { * 发起 Redis 分布式锁 * @return */ - @ApiOperation(value = "发起 Redis 分布式锁", notes = "发起 Redis 分布式锁") @GetMapping("/testLock") ResultVo testLock(); @@ -66,7 +66,6 @@ public interface TestApi { * 修改数据 * @return */ - @ApiOperation(value = "修改数据", notes = "修改数据") @GetMapping("/update") ResultVo update(TestModel entity); @@ -75,7 +74,6 @@ public interface TestApi { * 查看对象 * @return */ - @ApiOperation(value = "查看对象", notes = "查看对象") @GetMapping("/get") ResultVo get(TestModel entity); @@ -84,7 +82,6 @@ public interface TestApi { * 删除对象 * @return */ - @ApiOperation(value = "删除对象", notes = "删除对象") @GetMapping("/del") ResultVo del(String id); @@ -93,9 +90,35 @@ public interface TestApi { * 删除全部对象 * @return */ - @ApiOperation(value = "删除全部对象", notes = "删除全部对象") @GetMapping("/delAll") ResultVo delAll(); + /** + * 查找一个集合 + * @return + */ + @GetMapping("/findList") + ResultVo> findList(); + + + /** + * 查找一个全部集合 + * @return + */ + @GetMapping("/findAllList") + ResultVo> findAllList(); + + + /** + * 查找一个分页 + * @return + */ + @GetMapping("/findPage") + ResultVo findPage( + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + HttpServletRequest request + ); + } diff --git a/opsli-api/src/main/java/org/opsli/api/wrapper/test/TestModel.java b/opsli-api/src/main/java/org/opsli/api/wrapper/test/TestModel.java index b28319e7..432bb3bf 100644 --- a/opsli-api/src/main/java/org/opsli/api/wrapper/test/TestModel.java +++ b/opsli-api/src/main/java/org/opsli/api/wrapper/test/TestModel.java @@ -1,5 +1,6 @@ package org.opsli.api.wrapper.test; +import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.opsli.api.base.warpper.ApiWrapper; @@ -11,6 +12,7 @@ import org.opsli.api.base.warpper.ApiWrapper; * @CreateTime: 2020-09-16 17:33 * @Description: 测试类 */ +@ApiModel(value="测试接口返回Model",description="测试接口返回Model") @Data public class TestModel extends ApiWrapper { diff --git a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/MyBatisConstants.java b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/MyBatisConstants.java index aa9455b1..32eb5d99 100644 --- a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/MyBatisConstants.java +++ b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/constants/MyBatisConstants.java @@ -20,7 +20,8 @@ public final class MyBatisConstants { public static final char LOGIC_NOT_DELETE_VALUE = prop.getChar("mybatis-plus.global-config.db-config.logic-not-delete-value",'0'); - + /** ID */ + public static final String FIELD_ID = "id"; /** 创建人 */ public static final String FIELD_CREATE_BY = "createBy"; /** 更新时间 */ @@ -33,6 +34,8 @@ public final class MyBatisConstants { public static final String FIELD_DELETE_LOGIC = "deleted"; /** 乐观锁 */ public static final String FIELD_OPTIMISTIC_LOCK = "version"; + /** 多租户字段 */ + public static final String FIELD_TENANT = "tenantId"; private MyBatisConstants(){} } diff --git a/opsli-base-support/opsli-common/src/main/java/org/opsli/common/utils/HumpUtil.java b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/utils/HumpUtil.java new file mode 100644 index 00000000..c311a0a2 --- /dev/null +++ b/opsli-base-support/opsli-common/src/main/java/org/opsli/common/utils/HumpUtil.java @@ -0,0 +1,71 @@ +package org.opsli.common.utils; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.common.utils + * @Author: Parker + * @CreateTime: 2020-09-19 23:21 + * @Description: 驼峰转换 + */ +public final class HumpUtil { + + private final static String UNDERLINE = "_"; + + + private HumpUtil(){} + + + /*** + * 下划线命名转为驼峰命名 + * + * @param para + * 下划线命名的字符串 + */ + + public static String underlineToHump(String para) { + StringBuilder result = new StringBuilder(); + String a[] = para.split(UNDERLINE); + for (String s : a) { + if (!para.contains(UNDERLINE)) { + result.append(s); + continue; + } + if (result.length() == 0) { + result.append(s.toLowerCase()); + } else { + result.append(s.substring(0, 1).toUpperCase()); + result.append(s.substring(1).toLowerCase()); + } + } + return result.toString(); + } + + /*** + * 驼峰命名转为下划线命名 + * + * @param para + * 驼峰命名的字符串 + */ + + public static String humpToUnderline(String para) { + StringBuilder sb = new StringBuilder(para); + int temp = 0;//定位 + if (!para.contains(UNDERLINE)) { + for (int i = 0; i < para.length(); i++) { + if (Character.isUpperCase(para.charAt(i))) { + sb.insert(i + temp, UNDERLINE); + temp += 1; + } + } + } + return sb.toString().toLowerCase(); + } + + public static void main(String[] args) { + String aa = HumpUtil.humpToUnderline("tenantId"); + String bb = HumpUtil.underlineToHump(aa); + System.out.println(aa); + System.out.println(bb); + } + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java index e01d1341..22cee14c 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/aspect/CacheDataAop.java @@ -113,6 +113,17 @@ public class CacheDataAop { return returnValue; } + // 删除状态判断 + try { + Boolean ret = (Boolean) returnValue; + if(ret == null || !ret){ + return returnValue; + } + }catch (Exception e){ + log.error(e.getMessage(),e); + return returnValue; + } + // ====== 如果 使用了 EnableHotData ,表示开启热数据加载 则执行下段代码 List cacheDataEntityList = this.delHandlerData(point, args); if(cacheDataEntityList == null || cacheDataEntityList.size() == 0){ diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/concroller/BaseRestController.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/concroller/BaseRestController.java index eda8d831..0752c871 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/concroller/BaseRestController.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/concroller/BaseRestController.java @@ -1,6 +1,7 @@ package org.opsli.core.base.concroller; +import cn.hutool.core.util.TypeUtil; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.opsli.api.base.warpper.ApiWrapper; @@ -17,7 +18,7 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.PostConstruct; -import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; /** * @BelongsProject: opsli-boot @@ -38,8 +39,12 @@ public abstract class BaseRestController modelClazz; + /** Model 泛型游标 */ + private static final int modelIndex = 0; /** Entity Clazz 类 */ protected Class entityClazz; + /** Entity 泛型游标 */ + private static final int entityIndex = 1; @Autowired(required = false) protected S IService; @@ -69,6 +74,8 @@ public abstract class BaseRestController getModelClass(){ - Class tClass = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + Class tClass = null; + Type typeArgument = TypeUtil.getTypeArgument(getClass().getGenericSuperclass(), modelIndex); + if(typeArgument != null){ + tClass = (Class) typeArgument; + } return tClass; } @@ -136,7 +147,11 @@ public abstract class BaseRestController getEntityClass(){ - Class tClass = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[1]; + Class tClass = null; + Type typeArgument = TypeUtil.getTypeArgument(getClass().getGenericSuperclass(), entityIndex); + if(typeArgument != null){ + tClass = (Class) typeArgument; + } return tClass; } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/entity/BaseEntity.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/entity/BaseEntity.java index 33f11ad4..277d7650 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/entity/BaseEntity.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/entity/BaseEntity.java @@ -1,6 +1,5 @@ package org.opsli.core.base.entity; -import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; @@ -35,4 +34,20 @@ public abstract class BaseEntity extends ApiWrapper { **/ + + + /** + * 多租户 ID ,如果要使用多租户模式的话 子类必须重写父类字段 + * 且 数据库 有 tenant_id 字段 varchar类型 32 位 + * + * 只需要加载 Entity上 Wrapper的Model不需要加字段,如果没有 tenantId 字段默认不是租户模式 + * 且不可为空,为空的字段当数据量大起来时 查询会影响效率 + */ + /** + + private String tenantId; + + */ + + } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/impl/CrudServiceImpl.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/impl/CrudServiceImpl.java index b300e20d..ddb4f09a 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/impl/CrudServiceImpl.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/impl/CrudServiceImpl.java @@ -1,20 +1,27 @@ package org.opsli.core.base.service.impl; -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.TypeUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.github.pagehelper.PageInfo; import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; import org.opsli.api.base.warpper.ApiWrapper; -import org.opsli.common.annotation.EnableHotData; import org.opsli.common.constants.MyBatisConstants; +import org.opsli.common.utils.HumpUtil; import org.opsli.common.utils.WrapperUtil; import org.opsli.core.base.entity.BaseEntity; import org.opsli.core.base.service.base.BaseService; import org.opsli.core.base.service.interfaces.CrudServiceInterface; +import org.opsli.core.persistence.Page; +import org.opsli.core.utils.UserUtil; import javax.annotation.PostConstruct; import java.lang.reflect.ParameterizedType; -import java.util.Arrays; +import java.lang.reflect.Type; import java.util.Collection; import java.util.List; @@ -31,21 +38,33 @@ import java.util.List; * * 没有 直接用一个save类的原因,可能是觉得 新增和修改分开一些会比较好 防止串了数据 * + * 批量查询 做了多租户判断 + * 单独按照ID查询数据 和 按照ID修改、删除数据 隔离级别暂时不需要 + * 既然能从page列表中看到的数据 则是这个租户的数据 + * */ @Slf4j public abstract class CrudServiceImpl, E extends ApiWrapper, T extends BaseEntity> extends BaseService implements CrudServiceInterface { - /** entity Class 类 */ - protected Class entityClazz; - /** entity Class 类 */ + /** Model Clazz 类 */ protected Class modelClazz; + /** Model 泛型游标 */ + private static final int modelIndex = 1; + /** Entity Clazz 类 */ + protected Class entityClazz; + /** Entity 泛型游标 */ + private static final int entityIndex = 2; + + + /** 多租户状态 */ + protected boolean tenantFlag = false; @Override public E get(String id) { return transformT2M( - baseMapper.selectById(id) + super.getById(id) ); } @@ -53,7 +72,7 @@ public abstract class CrudServiceImpl, E extends ApiWrap public E get(E model) { if(model == null) return null; return transformT2M( - baseMapper.selectById(model.getId()) + super.getById(model.getId()) ); } @@ -61,8 +80,8 @@ public abstract class CrudServiceImpl, E extends ApiWrap public E insert(E model) { if(model == null) return null; T entity = transformM2T(model); - int count = baseMapper.insert(entity); - if(count > 0){ + boolean ret = super.save(entity); + if(ret){ return transformT2M(entity); } return null; @@ -72,44 +91,78 @@ public abstract class CrudServiceImpl, E extends ApiWrap public E update(E model) { if(model == null) return null; T entity = transformM2T(model); - int count = baseMapper.updateById(entity); - if(count > 0){ + boolean ret = super.updateById(entity); + if(ret){ return transformT2M(entity); } return null; } @Override - public int delete(String id) { - return baseMapper.deleteById(id); + public boolean delete(String id) { + return super.removeById(id); } @Override - public int delete(E model) { - if(model == null) return 0; - return baseMapper.deleteById(model.getId()); + public boolean delete(E model) { + if(model == null) return false; + return super.removeById(model.getId()); } @Override - public int deleteAll(String[] ids) { - if(ids == null) return 0; - List idList = Arrays.asList(ids); - return baseMapper.deleteBatchIds(idList); + public boolean deleteAll(String[] ids) { + if(ids == null) return false; + List idList = Convert.toList(String.class, ids); + return super.removeByIds(idList); } @Override - public int deleteAll(Collection models) { - if(models == null || models.isEmpty()) return 0; + public boolean deleteAll(Collection models) { + if(models == null || models.isEmpty()) return false; List idList = Lists.newArrayListWithCapacity(models.size()); for (E entity : models) { idList.add(entity.getId()); } - return baseMapper.deleteBatchIds(idList); + return super.removeByIds(idList); } @Override - public List findList(E model) { - return null; + public List findList(QueryWrapper queryWrapper) { + // 判断多租户 + if(this.tenantFlag) { + String tenantId = UserUtil.getTenantId(); + if (StringUtils.isNotEmpty(tenantId)) { + queryWrapper.eq(HumpUtil.humpToUnderline(MyBatisConstants.FIELD_TENANT), tenantId); + } + } + return super.list(queryWrapper); + } + + @Override + public List findAllList() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + // 判断多租户 + if(this.tenantFlag){ + String tenantId = UserUtil.getTenantId(); + if(StringUtils.isNotEmpty(tenantId)){ + queryWrapper.eq(HumpUtil.humpToUnderline(MyBatisConstants.FIELD_TENANT), tenantId); + } + } + return super.list(queryWrapper); + } + + @Override + public Page findPage(Page page) { + page.pageHelperBegin(); + try{ + List list = this.findList(page.getQueryWrapper()); + List es = WrapperUtil.transformInstance(list, modelClazz); + PageInfo pageInfo = new PageInfo<>(es); + page.instance(pageInfo); + } finally { + page.pageHelperEnd(); + } + return page; } // ======================== 对象转化 ======================== @@ -164,17 +217,24 @@ public abstract class CrudServiceImpl, E extends ApiWrap try { this.modelClazz = this.getModelClass(); this.entityClazz = this.getEntityClass(); + // 多租户判断 + this.tenantFlag = ReflectUtil.hasField(entityClazz, MyBatisConstants.FIELD_TENANT); }catch (Exception e){ log.error(e.getMessage(),e); } } + /** * 获得 泛型 Clazz * @return */ private Class getModelClass(){ - Class tClass = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[1]; + Class tClass = null; + Type typeArgument = TypeUtil.getTypeArgument(getClass().getGenericSuperclass(), modelIndex); + if(typeArgument != null){ + tClass = (Class) typeArgument; + } return tClass; } @@ -183,7 +243,11 @@ public abstract class CrudServiceImpl, E extends ApiWrap * @return */ private Class getEntityClass(){ - Class tClass = (Class)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[2]; + Class tClass = null; + Type typeArgument = TypeUtil.getTypeArgument(getClass().getGenericSuperclass(), entityIndex); + if(typeArgument != null){ + tClass = (Class) typeArgument; + } return tClass; } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/interfaces/CrudServiceInterface.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/interfaces/CrudServiceInterface.java index 05181bc2..e5ff3b76 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/interfaces/CrudServiceInterface.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/base/service/interfaces/CrudServiceInterface.java @@ -2,7 +2,8 @@ package org.opsli.core.base.service.interfaces; -import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import org.opsli.core.persistence.Page; import java.util.Collection; import java.util.List; @@ -26,16 +27,16 @@ public interface CrudServiceInterface extends BaseServiceInterface { /** * 获取单条数据 * - * @param id - * @return + * @param id ID + * @return E */ E get(String id); /** * 获取单条数据 * - * @param model - * @return + * @param model model 数据模型 + * @return E */ E get(E model); @@ -48,8 +49,8 @@ public interface CrudServiceInterface extends BaseServiceInterface { * 能往下走的只能是成功 * 异常问题 已经统一被处理 * - * @param model - * @return + * @param model model 数据模型 + * @return E */ E insert(E model); @@ -61,8 +62,8 @@ public interface CrudServiceInterface extends BaseServiceInterface { * 能往下走的只能是成功 * 异常问题 已经统一被处理 * - * @param model - * @return + * @param model model 数据模型 + * @return E */ E update(E model); @@ -75,11 +76,10 @@ public interface CrudServiceInterface extends BaseServiceInterface { * 能往下走的只能是成功 * 异常问题 已经统一被处理 * - * @param id - * @return - * @see int delete(T entity) + * @param id ID + * @return boolean */ - int delete(String id); + boolean delete(String id); /** @@ -90,10 +90,10 @@ public interface CrudServiceInterface extends BaseServiceInterface { * 能往下走的只能是成功 * 异常问题 已经统一被处理 * - * @param model - * @return + * @param model 数据模型 + * @return boolean */ - int delete(E model); + boolean delete(E model); /** @@ -104,10 +104,10 @@ public interface CrudServiceInterface extends BaseServiceInterface { * 能往下走的只能是成功 * 异常问题 已经统一被处理 * - * @param ids - * @return + * @param ids id数组 + * @return boolean */ - int deleteAll(String[] ids); + boolean deleteAll(String[] ids); /** * 批量物理删除 @@ -117,27 +117,34 @@ public interface CrudServiceInterface extends BaseServiceInterface { * 能往下走的只能是成功 * 异常问题 已经统一被处理 * - * @param models - * @return + * @param models 封装模型 + * @return boolean */ - int deleteAll(Collection models); + boolean deleteAll(Collection models); /** * 查询数据列表 * - * @param model - * @return + * @param queryWrapper 查询条件构造器 + * @return List */ - List findList(E model); + List findList(QueryWrapper queryWrapper); + + + /** + * 查询全部数据列表 + * + * @return List + */ + List findAllList(); /** - * 查询所有数据列表 + * 查询分页数据 * - * @return - * @see List findAllList(T entity) + * @return Page */ - //Page findPage(Page page, T t); + Page findPage(Page page); } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/conf/mybatis/AutoFillInterceptor.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/conf/mybatis/AutoFillInterceptor.java index 28bef600..0d019b82 100644 --- a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/conf/mybatis/AutoFillInterceptor.java +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/conf/mybatis/AutoFillInterceptor.java @@ -9,6 +9,7 @@ import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.plugin.*; import org.opsli.common.constants.MyBatisConstants; +import org.opsli.core.utils.UserUtil; import org.springframework.stereotype.Component; import java.lang.reflect.Field; @@ -20,6 +21,12 @@ import java.util.*; * * PS:Plus中的自动注入器太难用了 * + * -- 多租户设置 当前方案选择的是按照表加 租户字段 + * 如果租户数量过大 可考虑按照 业务分库 再选择横纵拆表 然后再按照表中租户分区 可以缓解一下数量问题 + * 多租户要考虑数据隔离级别 这里选择的是 按照分页进行隔离,毕竟对于客户来讲,只能看到分页的数据 + * 也就是说 要控制再 findList层 + * 自定义查询SQL的话 一定要注意 , 如果有租户设置 一定要加上多租户查询 + * * 参考地址:https://www.cnblogs.com/qingshan-tang/p/13299701.html */ @Component @@ -100,6 +107,10 @@ public class AutoFillInterceptor implements Interceptor { case MyBatisConstants.FIELD_DELETE_LOGIC: setProperty(arg, MyBatisConstants.FIELD_DELETE_LOGIC, MyBatisConstants.LOGIC_NOT_DELETE_VALUE); break; + // 多租户设置 + case MyBatisConstants.FIELD_TENANT: + setProperty(arg, MyBatisConstants.FIELD_TENANT, UserUtil.getTenantId()); + break; default: break; } diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/persistence/Page.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/persistence/Page.java new file mode 100644 index 00000000..0961bdaf --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/persistence/Page.java @@ -0,0 +1,123 @@ +package org.opsli.core.persistence; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.github.pagehelper.PageSerializable; + +import java.util.HashMap; +import java.util.Map; + +public class Page extends PageSerializable{ + + + private int pageNo = 1; + private int pageSize = 10; + /** 查询条件构造器 */ + private QueryWrapper queryWrapper; + + public Page(){ + super(); + } + + /** + * 分页 构造函数 + * @param pageNo 页 + * @param pageSize 分页大小 + */ + public Page(int pageNo, int pageSize) { + super(); + this.pageNo = pageNo; + this.pageSize = pageSize; + } + + /** + * 分页 构造函数 + * @param pageNo 页 + * @param pageSize 分页大小 + * @param queryWrapper 查询条件 + */ + public Page(int pageNo, int pageSize, QueryWrapper queryWrapper) { + super(); + this.pageNo = pageNo; + this.pageSize = pageSize; + this.queryWrapper = queryWrapper; + } + + /** + * 分页 构造函数 + * @param pageNo 页 + * @param pageSize 分页大小 + */ + public Page(int pageNo, int pageSize, String orderBy) { + super(); + this.pageNo = pageNo; + this.pageSize = pageSize; + } + + + + /** + * 分页函数 + */ + public void pageHelperBegin(){ + PageHelper.startPage(this.pageNo,this.pageSize); + } + + /** + * 分页函数 + */ + public void pageHelperEnd(){ + PageHelper.clearPage(); + } + + + /** + * 设置数据 + * @param pageInfo + */ + public void instance(PageInfo pageInfo) { + super.setList(pageInfo.getList()); + super.setTotal(pageInfo.getTotal()); + } + + + /** + * 获取bootstrap data分页数据 + * @return map对象 + */ + public Map getBootstrapData(){ + Map map = new HashMap<>(); + map.put("rows", this.getList()); + map.put("total", this.getTotal()); + return map; + } + + + // ======================================================= + + public int getPageNo() { + return pageNo; + } + + public void setPageNo(int pageNo) { + this.pageNo = pageNo; + } + + public int getPageSize() { + return pageSize; + } + + public void setPageSize(int pageSize) { + this.pageSize = pageSize; + } + + public QueryWrapper getQueryWrapper() { + return queryWrapper; + } + + public void setQueryWrapper(QueryWrapper queryWrapper) { + this.queryWrapper = queryWrapper; + } + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/persistence/PageQueryBuilder.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/persistence/PageQueryBuilder.java new file mode 100644 index 00000000..a1d1c8bb --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/persistence/PageQueryBuilder.java @@ -0,0 +1,198 @@ +package org.opsli.core.persistence; + +import cn.hutool.core.util.ReflectUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.opsli.api.base.warpper.ApiWrapper; +import org.opsli.common.utils.HumpUtil; +import org.opsli.core.base.entity.BaseEntity; + +import java.util.Map; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.persistence + * @Author: Parker + * @CreateTime: 2020-09-19 21:15 + * @Description: 查询构建器 + * + * 针对分页查询 无非也就是 + * 全值匹配 eq + * 模糊匹配 like + * 日期 begin end 匹配 + * + */ +@Slf4j +public class PageQueryBuilder{ + + + // == 匹配条件 == + /** 全值匹配 */ + private static final String EQ = "EQ"; + /** 模糊匹配 */ + private static final String LIKE = "LIKE"; + /** 日期匹配 */ + private static final String BEGIN = "BEGIN"; + private static final String END = "END"; + /** 排序方式 */ + private static final String ORDER = "ORDER"; + private static final String ORDER_ASC = "ASC"; + private static final String ORDER_DESC = "DESC"; + + + /** 当前页 */ + private Integer pageNo; + /** 每页数量 */ + private Integer pageSize; + /** 参数 */ + private Map parameterMap; + /** Entity Clazz */ + private Class entityClazz; + + /** + * 构造函数 + * @param entityClazz Entity 的 clazz + * @param pageNo 当前页 + * @param pageSize 每页显示条数 + * @param parameterMap request 参数 + */ + public PageQueryBuilder(Class entityClazz, Integer pageNo, Integer pageSize, Map parameterMap){ + this.pageNo = pageNo; + this.pageSize = pageSize; + this.parameterMap = parameterMap; + this.entityClazz = entityClazz; + } + + /** + * 构建builderPage + * @return + */ + public Page builderPage(){ + Page page = new Page<>(this.pageNo,this.pageSize); + QueryWrapper queryWrapper = this.createQueryWrapper(); + page.setQueryWrapper(queryWrapper); + return page; + } + + /** + * 创建 查询条件构造器 + * @return + */ + private QueryWrapper createQueryWrapper(){ + QueryWrapper queryWrapper = new QueryWrapper<>(); + if(this.parameterMap == null){ + return queryWrapper; + } + for (Map.Entry stringEntry : this.parameterMap.entrySet()) { + String keys = stringEntry.getKey(); + String[] values = stringEntry.getValue(); + // 非空检测 + if(StringUtils.isEmpty(keys) || values == null || StringUtils.isEmpty(values[0])){ + continue; + } + + // 键 和 操作 + String[] key_handle = keys.split("_"); + if(key_handle.length < 2){ + continue; + } + // 判断 字段是否合法 + boolean hasField = this.validationField(key_handle); + if(hasField){ + // 验证操作是否合法 + boolean hasHandle = this.validationHandle(key_handle); + if(hasHandle){ + // 操作 + String handle = key_handle[1]; + // 键 + String key = key_handle[0]; + // 处理值 + String value = values[0]; + // 赋值 + this.handlerValue(queryWrapper, handle, key ,value); + } + } + } + return queryWrapper; + } + + /** + * 处理值 + * @param queryWrapper 查询构造器 + * @param handle 操作 + * @param key 键 + * @param value 值 + * @return + */ + private void handlerValue(QueryWrapper queryWrapper, String handle, String key, String value){ + if(queryWrapper == null || StringUtils.isEmpty(handle) + || StringUtils.isEmpty(key) || StringUtils.isEmpty(value) + ){ + return; + } + // 转换驼峰 为 数据库下划线字段 + key = HumpUtil.humpToUnderline(key); + if (EQ.equals(handle)) { + // 全值匹配 + queryWrapper.eq(key,value); + } else if (LIKE.equals(handle)) { + // 模糊匹配 + queryWrapper.like(key,value); + } else if (BEGIN.equals(handle)) { + // 大于等于 + queryWrapper.ge(key,value); + } else if (END.equals(handle)) { + // 小于等于 + queryWrapper.le(key,value); + } else if (ORDER.equals(handle)) { + // 排序 + if(ORDER_ASC.equals(value)){ + queryWrapper.orderByAsc(key); + } else if(ORDER_DESC.equals(value)){ + queryWrapper.orderByDesc(key); + } else{ + queryWrapper.orderByAsc(key); + } + } + } + + /** + * 检测 字段是否合法 + * @param key_handle + * @return + */ + private boolean validationField(String[] key_handle){ + if(entityClazz == null || key_handle == null || StringUtils.isEmpty(key_handle[0])){ + return false; + } + // 判断当前传入参数 是否是Entity的字段 + return ReflectUtil.hasField(entityClazz, key_handle[0]); + } + + + /** + * 检测 操作是否合法 + * @param key_handle + * @return + */ + private boolean validationHandle(String[] key_handle){ + if(key_handle == null || StringUtils.isEmpty(key_handle[1])){ + return false; + } + String handle = key_handle[1]; + if (EQ.equals(handle)) { + return true; + } else if (LIKE.equals(handle)) { + return true; + } else if (BEGIN.equals(handle)) { + return true; + } else if (END.equals(handle)) { + return true; + } else if (ORDER.equals(handle)) { + return true; + } + return false; + } + +} diff --git a/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java new file mode 100644 index 00000000..176e17d1 --- /dev/null +++ b/opsli-base-support/opsli-core/src/main/java/org/opsli/core/utils/UserUtil.java @@ -0,0 +1,26 @@ +package org.opsli.core.utils; + +/** + * @BelongsProject: opsli-boot + * @BelongsPackage: org.opsli.core.utils + * @Author: Parker + * @CreateTime: 2020-09-19 20:03 + * @Description: 用户工具类 + */ +public final class UserUtil { + + private UserUtil(){} + + /** + * 获得 租户ID + * @return + */ + public static String getTenantId(){ + // TODO 如果 没取到多租户ID 也按照默认值赋值 且不可删除默认多租户数据 + // TODO 判断权限 如果是 admin 超级管理员 则租户ID清空 且findList 不做处理 否则默认都会做处理 + // TODO 如果表中 没有 tenant_id 字段 则不进行多租户处理 + + return "a121321255"; + } + +} diff --git a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java index ec55d4ca..b4415710 100644 --- a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java +++ b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/entity/TestEntity.java @@ -1,7 +1,7 @@ package org.opsli.modulars.test.entity; -import io.swagger.annotations.ApiModelProperty; import lombok.Data; +import lombok.EqualsAndHashCode; import org.opsli.core.base.entity.BaseEntity; /** @@ -12,12 +12,16 @@ import org.opsli.core.base.entity.BaseEntity; * @Description: 测试类 */ @Data +@EqualsAndHashCode(callSuper = false) public class TestEntity extends BaseEntity { - @ApiModelProperty(value = "名称") + /** 名称 */ private String name; - @ApiModelProperty(value = "备注") + /** 备注 */ private String remark; + /** 多租户字段 */ + private String tenantId; + } diff --git a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/impl/TestServiceImpl.java b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/impl/TestServiceImpl.java index e0606353..f91fb5c0 100644 --- a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/impl/TestServiceImpl.java +++ b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/service/impl/TestServiceImpl.java @@ -59,25 +59,25 @@ public class TestServiceImpl extends CrudServiceImpl models) { + public boolean deleteAll(Collection models) { return super.deleteAll(models); } diff --git a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestRestController.java b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestRestController.java index 8d42b768..2d50d41a 100644 --- a/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestRestController.java +++ b/opsli-modulars/opsli-modulars-test/src/main/java/org/opsli/modulars/test/web/TestRestRestController.java @@ -1,15 +1,19 @@ package org.opsli.modulars.test.web; import cn.hutool.core.thread.ThreadUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import io.swagger.annotations.ApiOperation; import org.apache.commons.lang3.StringUtils; import org.opsli.api.base.result.ResultVo; import org.opsli.api.wrapper.test.TestModel; import org.opsli.api.web.test.TestApi; import org.opsli.common.annotation.ApiRestController; +import org.opsli.common.utils.WrapperUtil; import org.opsli.core.base.concroller.BaseRestController; import org.opsli.core.cache.pushsub.enums.CacheType; import org.opsli.core.cache.pushsub.msgs.DictMsgFactory; +import org.opsli.core.persistence.Page; +import org.opsli.core.persistence.PageQueryBuilder; import org.opsli.modulars.test.entity.TestEntity; import org.opsli.modulars.test.service.ITestService; import org.opsli.plugins.mail.MailPlugin; @@ -20,6 +24,7 @@ import org.opsli.plugins.redis.lock.RedisLock; import org.opsli.plugins.redis.pushsub.entity.BaseSubMessage; import org.springframework.beans.factory.annotation.Autowired; +import javax.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; import java.util.Random; @@ -227,5 +232,31 @@ public class TestRestRestController extends BaseRestController> findList() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + List list = IService.findList(queryWrapper); + List testModels = WrapperUtil.transformInstance(list, TestModel.class); + return ResultVo.success(testModels); + } + + @ApiOperation(value = "查找全部数据", notes = "查找全部数据") + @Override + public ResultVo> findAllList() { + List list = IService.findAllList(); + List testModels = WrapperUtil.transformInstance(list, TestModel.class); + return ResultVo.success(testModels); + } + + @ApiOperation(value = "查询分页", notes = "查询分页") + @Override + public ResultVo findPage(Integer pageNo, Integer pageSize, HttpServletRequest request) { + PageQueryBuilder pageQueryBuilder = new PageQueryBuilder<>( + TestEntity.class, pageNo, pageSize, request.getParameterMap() + ); + Page page = IService.findPage(pageQueryBuilder.builderPage()); + return ResultVo.success(page.getBootstrapData()); + } } diff --git a/pom.xml b/pom.xml index d1ba5781..c346fe06 100644 --- a/pom.xml +++ b/pom.xml @@ -146,6 +146,20 @@ ${mybatis-plus.version} + + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.3.0 + + + + + + + + + com.alibaba