一起用注解过滤数据吧

背景


工作中肯定用到根据某某机构去过滤数据也就是权限控制
这类数据想当然就应该用 SpringAOP 去实现

思路


首先规定用什么标志让 Spring 识别当前方法需要数据过滤
优雅的使用自定义注解是个不错的选择
注解里面传入当前要进行数据过滤的 Class 对象即可
业务实体类对象里面需要声明一份公共的 OrgIds List 格式用来存放过滤后的查询条件

自定义 DataFilter 注解


package ;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import cn.hutool.core.util.ReflectUtil;

/**
 * 数据过滤, 默认过滤组织机构 </br>
 * 可以指定要过滤字段 filterField = { FilterStrategy.orgIds, FilterStrategy.projectIds }
 * 
 * @author 
 * @since 2021-09-19
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataFilter {
	
	/**
	 * 要执行数据过滤的对象
	 * 
	 * @return
	 */
	Class<?> value();
	
	/**
	 * 要执行数据过滤的字段
	 * 
	 * @return
	 */
	FilterStrategy[] filterField() default FilterStrategy.orgIds;
	
	/**
	 * 数据过滤字段策略
	 */
	enum FilterStrategy {
		
		/**
		 * 根据组织机构ID过滤数据
		 */
		orgIds,
		
		/**
		 * 根据项目ID过滤数据
		 */
		projectIds;
	}
	
	/**
	 * 机构查询参数枚举
	 */
	enum OrgQueryParam {
		
		/**
		 * 根据查询对象orgId字段过滤数据
		 */
		orgId,
		
		/**
		 * 根据查询对象did字段过滤数据
		 */
		did,
		
		/**
		 * 根据查询对象createOrgId字段过滤数据
		 */
		createOrgId,

		/**
		 * 根据查询对象orgName字段过滤数据
		 */
		orgName;
		
		/**
		 * 过滤有值字段
		 * 
		 * @param obj
		 * @return
		 */
		public static Object fieldHasValue(Object obj) {
			for (OrgQueryParam queryParam : OrgQueryParam.values()) {
				Object fieldValue = ReflectUtil.getFieldValue(obj, queryParam.name());
				if (fieldValue != null) {
					return fieldValue;
				}
			}
			return null;
		}
		
		/**
		 * 查询条件字段置空
		 * 
		 * @param obj
		 * @return
		 */
		public static void fieldSetNull(Object obj) {
			for (OrgQueryParam queryParam : OrgQueryParam.values()) {
				Object fieldValue = ReflectUtil.getFieldValue(obj, queryParam.name());
				if (fieldValue != null) {
					ReflectUtil.setFieldValue(obj, queryParam.name(), null);
				}
			}
		}
	}
}

数据过滤切面


package ;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

import cn.com.insurance.admin.internal.annotation.DataFilter;
import cn.com.insurance.admin.internal.annotation.DataFilter.FilterStrategy;
import cn.com.insurance.admin.internal.annotation.DataFilter.OrgQueryParam;
import cn.com.insurance.admin.internal.shiro.ShiroContext;
import cn.com.insurance.admin.internal.utils.StringUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ReflectUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 数据过滤切面
 * 
 * @author 
 * @since 2021-09-19
 */
@Slf4j
@Aspect
@Component
public class DataFilterAspect {
	
	/**
	 * 操作切入点,一般情况该方法中再不需要添加其他的代码 使用@Pointcut 来声明切入点表达式 后面的其他通知直接使用方法名直接引用方法名即可
	 */
	@Pointcut("@annotation(cn.com.insurance.admin.internal.annotation.DataFilter)")
	public void operatePointExpression() {
	}
	
	/**
	 * 环绕
	 * 
	 * @param joinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around("operatePointExpression()")
	public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
		try {
			Object[] args = joinPoint.getArgs();// 获取参数
			// 超级管理员不进行数据过滤
			boolean isAdmin = ShiroContext.getCurrUser() == null || ShiroContext.getCurrUser().isSuperAdmin();
			if (isAdmin) {
				log.debug("数据过滤执行结果: 当前登录用户是超级管理员: {},过滤参数是: \r\n{}",
						ShiroContext.getCurrUser(),
						JSON.toJSONString(args, true));
			}
			Class<?> classTarget = joinPoint.getTarget().getClass();// 获取目标对象
			Class<?>[] par = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();// 获取参数类型
			Method objMethod = classTarget.getMethod(joinPoint.getSignature().getName(), par);
			DataFilter annotation = objMethod.getAnnotation(DataFilter.class);
			
			doDataFilter(args, annotation, isAdmin);
			
			log.debug("数据过滤执行结果: 当前登录用户是: {},过滤参数是: \r\n{}", ShiroContext.getCurrUser(), JSON.toJSONString(args, true));
		} catch (Exception e) {
			log.error("数据过滤异常: {}", e);
		}
		return joinPoint.proceed();
	}
	
	/**
	 * 执行数据过滤
	 * 
	 * @param args       方法参数列表
	 * @param annotation 数据过滤注解
	 * @param isAdmin
	 */
	@SuppressWarnings("unchecked")
	private void doDataFilter(Object[] args, DataFilter annotation, boolean isAdmin) {
		Class<? extends Object> clazz = annotation.value();
		for (Object obj : args) {
			// 匹配数据过滤对象
			if (obj.getClass() != clazz) {
				continue;
			}
			// 控制查看机构下级
			Object queryFieldHasValue = OrgQueryParam.fieldHasValue(obj);
			Object hasSubOrg = ReflectUtil.getFieldValue(obj, "hasSubOrg");
			// 1判断前端是否勾选:(含下级选项) 同时判断即使勾选下级但选择组织没有下级也视为未勾选下级
			// 2判断逻辑 非超级管理员
			// 3查询参数有值, 判断当前用户授权机构信息(sys_user_permission)
			// 4以授权机构为主 查询参数为辅
			if (hasSubOrg != null
					&& (boolean) hasSubOrg
						&& queryFieldHasValue != null
						&& ShiroContext.getSubOrgs((String) queryFieldHasValue).size() > 1) {
				ReflectUtil.invoke(obj,
						"set" + StringUtil.upperFirstChar(FilterStrategy.orgIds.name()),
						CollectionUtil.isNotEmpty(ShiroContext.getOrgIds()) && !isAdmin ? ShiroContext
								.getOrgIds() : ShiroContext.getSubOrgs((String) queryFieldHasValue));
				OrgQueryParam.fieldSetNull(obj);
			} else {
				// 5前端未勾选:(含下级选项) 但是查询参数有值
				if (queryFieldHasValue != null && queryFieldHasValue.toString().trim().length() > 0) {
					ReflectUtil.invoke(obj,
							"set" + StringUtil.upperFirstChar(FilterStrategy.orgIds.name()),
							Arrays.asList(queryFieldHasValue));
					OrgQueryParam.fieldSetNull(obj);
				}
			}
			List<String> methods = Arrays.stream(ReflectUtil.getMethodsDirectly(obj.getClass(), true))
					.map(Method::getName)
					.collect(Collectors.toList());
			for (FilterStrategy strategy : annotation.filterField()) {
				String field = strategy.name();
				// 给字段赋值
				String methodName = "set" + StringUtil.upperFirstChar(field);
				if (methods.contains(methodName) && field.contains(FilterStrategy.orgIds.name())) {
					// 机构ID 这里判断上面查询参数没有值的情况给与默认值赋值
					Object fieldValue = ReflectUtil.getFieldValue(obj, field);
					if (!isAdmin // 不是超级管理员且对应字段没有值
							&& (fieldValue == null
									|| (fieldValue instanceof Collection
											&& ((Collection<String>) fieldValue).isEmpty()))) {
						ReflectUtil.invoke(obj, methodName, ShiroContext.getOrgIds());
						OrgQueryParam.fieldSetNull(obj);
					}
				} else if (methods.contains(methodName) && field.contains(FilterStrategy.projectIds.name())) {
					// 项目ID
					ReflectUtil.invoke(obj, methodName, ShiroContext.getProjectIds());
				}
			}
		}
	}
}


公共Vo


package ;

import java.io.Serializable;
import java.util.List;

import javax.persistence.Transient;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;

import cn.com.insurance.admin.utils.response.Pagination;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.Accessors;

/**
 * 公共vo存放分页信息
 * 
 * @author 
 * @since 2021年4月27日14:39:39
 */

@ToString
@Accessors(chain = true)
@EqualsAndHashCode
@JsonIgnoreProperties("pagination")
public class CommonVo implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	@Transient
	@JsonInclude(JsonInclude.Include.NON_NULL)
	private Integer pageNo, pageSize;
	
	@Transient
	@JSONField(serialize = false)
	private Pagination pagination = new Pagination();
	
	/**
	 * projectIds 权限控制初始化列表查询
	 */
	@Transient
	private List<String> projectIds;
	
	/**
	 * projectIds 权限控制初始化列表查询
	 */
	@Transient
	private List<String> orgIds;
	
	/**
	 * hasSubOrg 权限控制组织机构下级查询
	 */
	@Transient
	private boolean hasSubOrg;
	
	public Integer getPageNo() {
		return pageNo;
	}
	
	public void setPageNo(Integer pageNo) {
		this.pageNo = pageNo;
		this.pagination.setCurrentPage(pageNo);
	}
	
	public Integer getPageSize() {
		return pageSize;
	}
	
	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
		this.pagination.setPageSize(pageSize);
	}
	
	public Pagination getPagination() {
		return pagination;
	}
	
	public void setPagination(Pagination pagination) {
		this.pagination = pagination;
	}
	
	public List<String> getProjectIds() {
		return projectIds;
	}
	
	public void setProjectIds(List<String> projectIds) {
		this.projectIds = projectIds;
	}
	
	public List<String> getOrgIds() {
		return orgIds;
	}
	
	public void setOrgIds(List<String> orgIds) {
		this.orgIds = orgIds;
	}
	
	public boolean isHasSubOrg() {
		return hasSubOrg;
	}
	
	public void setHasSubOrg(boolean hasSubOrg) {
		this.hasSubOrg = hasSubOrg;
	}
	
}


公共 Pagination


package ;

import java.io.Serializable;
import java.util.Objects;

/**
 * 分页处理器,非线程安全
 * 
 * @author 
 * @since 2021年5月12日15:53:12
 */
public class Pagination implements Serializable {
	/**
	 * serialVersionUID
	 */
	private static final long serialVersionUID = 6787772888002596631L;
	/**
	 * 当前记录索引
	 */
	private int currentIndex = 0;
	/**
	 * 当前页面,默认第一页。注意从1开始计算页码,而不是从0开始
	 */
	private int currentPage = 1;
	/**
	 * 总记录数
	 */
	private int totalRecords = -1;
	/**
	 * 每页显示记录数,默认10条
	 */
	private int pageSize = 10;
	
	/**
	 * 升/降序排列:desc/asc
	 */
	private SortItem sortItem = null;
	
	public Pagination() {
	}
	
	private boolean validate() {
		return this.totalRecords > -1;
	}
	
	public int getCurrentPage() {
		return this.currentPage;
	}
	
	/**
	 * 设置当前页。注意从1开始计算
	 * 
	 * @param currentPage
	 */
	public void setCurrentPage(int currentPage) {
		if (currentPage <= 0) {
			throw new IllegalArgumentException("the parameter 'currentPage' can not be zero or negative.");
		}
		this.currentPage = currentPage;
		if (validate()) {
			prepare();
		}
	}
	
	public int getCurrentIndex() {
		if (validate()) {
			prepare();
		}
		return this.currentIndex;
	}
	
	public int getTotalRecodes() {
		return this.totalRecords;
	}
	
	public void setTotalRecodes(int totalRecords) {
		if (totalRecords < 0) {
			totalRecords = 10;
//			throw new IllegalArgumentException(
//					"the parameter 'totalRecords' can not be negative:" + "[ totalRecords = " + totalRecords + " ]");
		}
		this.totalRecords = totalRecords;
		prepare();
	}
	
	public int getPageSize() {
		return this.pageSize;
	}
	
	public void setPageSize(int pageSize) {
		if (pageSize <= 0) {
			throw new IllegalArgumentException("the parameter 'pageSize' can not be zero or negative.");
		}
		this.pageSize = pageSize;
	}
	
	private void prepare() {
		if (this.totalRecords < 0) {
			totalRecords = 10;
//			throw new IllegalStateException(
//					"the field \"" + Pagination.class.getName() + ".totalRecords\" has not initialized.");
		}
		if (this.currentPage == 0) {
			this.currentPage = 1;
		}
		if (this.pageSize == 0) {
			this.pageSize = 10;
		}
		if (this.currentPage > this.getTotalPage()) {
			this.currentPage = this.getTotalPage();
		}
		this.currentIndex = ((this.currentPage - 1) * this.pageSize);
		if (this.currentIndex > this.totalRecords) {
			this.currentIndex = (this.totalRecords - (this.currentIndex - this.totalRecords));
		}
	}
	
	public int getTotalPage() {
		if (this.totalRecords <= this.pageSize) {
			return 1;
		}
		
		int m = this.totalRecords % this.pageSize;
		int totalPage = (this.totalRecords - m) / this.pageSize;
		if (m > 0) {
			totalPage++;
		}
		return totalPage;
	}
	
	/**
	 *
	 * @return
	 */
	public boolean isInitialized() {
		return this.validate();
	}
	
	/**
	 * do the same thing as this.setTotalRecodes(int totalRecords)
	 * 
	 * @param totalRecords
	 */
	public void init(int totalRecords) {
		this.setTotalRecodes(totalRecords);
	}
	
	public SortItem getSortItem() {
		return sortItem;
	}
	
	public void setSortItem(SortItem sortItem) {
		this.sortItem = sortItem;
	}
	
	/**
	 * 判断是否有下一页
	 * 
	 * @return
	 */
	public boolean hasNext() {
		int nextPage = this.currentPage + 1;
		int totalPage = getTotalPage();
		
		return nextPage <= totalPage;
	}
	
	/**
	 * 往后翻一页
	 */
	public void next() {
		if (hasNext()) {
			this.currentPage += 1;
		}
	}
	
	@Override
	public String toString() {
		return "Pagination{"
				+ "currentIndex="
					+ currentIndex
					+ ", currentPage="
					+ currentPage
					+ ", totalRecords="
					+ totalRecords
					+ ", pageSize="
					+ pageSize
					+ ", sortItem="
					+ sortItem
					+ '}';
	}
	
	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		Pagination that = (Pagination) o;
		return currentIndex == that.currentIndex
				&& currentPage == that.currentPage
					&& totalRecords == that.totalRecords
					&& pageSize == that.pageSize
					&& Objects.equals(sortItem, that.sortItem);
	}
	
	@Override
	public int hashCode() {
		return Objects.hash(currentIndex, currentPage, totalRecords, pageSize, sortItem);
	}
}

如何使用


	@DataFilter(CoreAccountingBill.class)
	@Override
	public List<CoreAccountingBill> queryCoreAccountingBillList(CoreAccountingBill coreAccountingBill, Pagination pagination) {
		PageInfo<CoreAccountingBill> pageInfo = PageHelper
				.startPage(pagination.getCurrentPage(), pagination.getPageSize())
				.doSelectPageInfo(() -> coreAccountingBillMapper.queryCoreAccountingBillList(coreAccountingBill));
		pagination.setTotalRecodes((int) pageInfo.getTotal());
	
		return pageInfo.getList();
	}
	

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要用三代测序数据组装出染色体级别的基因组,可以按照以下步骤进行: 1. 数据预处理:对三代测序数据进行质量控制和过滤,去除低质量和含有适配器的reads。 2. 组装:使用基因组组装软件对经过预处理的数据进行组装。由于三代测序数据具有较长的read长度和较高的错误率,因此需要使用适合处理这种数据的组装算法,如Flye、Canu、wtdbg2等。 3. 内部一致性校正:对组装结果进行内部一致性校正,去除矛盾的序列,提高组装准确性。 4. 粘连区域处理:在染色体级别组装过程中,常常会出现粘连区域,即存在多个不同的序列可以组装在一起。可以使用长读比对、Hi-C数据等方法进行粘连区域的处理,得到最终的染色体级别组装结果。 5. 评估和改进:对组装结果进行评估和改进,比较组装结果和已知参考基因组的差异,并使用其他数据如RNA-seq数据进行验证和改进。 以上是组装染色体级别基因组的一般步骤,具体实施中还需要结合具体的数据情况和组装软件的特点进行调整和优化。 ### 回答2: 染色体级别的基因组组装需要经过以下几个步骤: 1. 数据质控:首先对三代测序数据进行质控,包括去除低质量碱基、修剪末端序列、去除接头序列等处理,确保数据的准确性和完整性。 2. 参考基因组比对:使用相关物种的参考基因组作为参考,将测序reads与参考基因组进行比对。此步骤可使用一些开源的比对工具,如Bowtie、BWA等。 3. 去重和拼接:根据比对结果,对重复的读取进行去重,然后将比对上的reads进行拼接,生成更长的序列。常用的拼接工具有SPAdes、SOAPdenovo等。 4. 错误矫正:对拼接得到的长序列进行错误矫正,去除可能存在的测序错误。可使用Quiver、LoRDEC等工具进行错误矫正。 5. 碱基错误矫正:使用相关物种的其他基因组信息,如原核生物的拓扑结构、转录本序列等,进行碱基错误矫正,提高结果的准确性。可使用Pilon、Racon等工具进行碱基错误矫正。 6. 持续迭代:以上步骤可能需要多次迭代进行,直至获得较完整且准确的染色体级别基因组。 7. 结果评估:通过与已知基因组的比对、基因预测和注释等方式对组装结果进行评估,验证基因组的准确性和完整性。 总之,染色体级别基因组组装利用三代测序数据,通过质控、比对、拼接、错误矫正等多个步骤,最终得到较完整、准确的基因组序列。然而,组装结果仍需综合其他实验验证,才能确保基因组的完整性和准确性。 ### 回答3: 要组装一个染色体级别的基因组,首先需要收集足够的三代测序数据。三代测序技术包括Illumina,PacBio和Nanopore等,它们提供了高质量、长读长的测序数据。 第一步是建立一个参考基因组序列。可以使用辅助测序技术,如BioNano或Hi-C,来获得染色体的全长信息。这些信息将帮助将测序数据映射到参考基因组上。 接下来,将三代测序数据与参考序列进行比对。根据每个数据集之间的重叠区域,可以通过重叠改正和序列拼接方法将读取连接起来。通过比对多个数据集,可以提高准确性并填充序列间的空隙。 然后,进行读取错误矫正。三代测序技术由于其相对较高的错误率,可能需要采取矫正措施。可以使用PacBio和Nanopore提供的高质量排序读取来矫正Illumina数据集中的错误。 在得到组装的序列后,需要通过重叠区域检测和破碎区域映射来验证和填充序列。通过比对之前得到的长读取和映射的链接信息,可以检测到重叠和破碎区域,并进行修复和连接。 最后,继续进行序列校准和错误修复。可以使用基于概率的方法,如polish read or consensus correction,来矫正残留的序列错误。 通过这些步骤,我们可以逐渐组装出一个染色体级别的基因组。但需要明确的是,基因组组装是一个复杂的过程,可能涉及到很多细节和步骤。因此,在实际实施中,可能需要借助各种基因组组装软件和技术来完成任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值