当前位置: 首页 > news >正文

java 对mybatis拦截Interceptor进行权限控制(条件修改),入参修改,返回修改

使用jsqlparser对sql解析拼装

基础准备

<dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.5</version><scope>compile</scope>
</dependency>
<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.9.4</version>
</dependency>
import lombok.Data;
/*** 需要拦截或修改的值*/
@Data
public class InitiateInterceptField {public static final String deptId = "deptId";public static final String createBy = "createBy";public static final String updateBy = "updateBy";public static final String dept_id = "dept_id";public static final String create_by = "create_by";public static final String update_by = "update_by";public static final String deptName = "deptName";public static final String createName = "createName";public static final String updateName = "updateName";}
import cn.hutool.core.thread.ThreadFactoryBuilder;
import lombok.Data;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;import java.util.concurrent.*;@Data
@Component
public class DisclosureThreadPool {@Bean("mybatisInterceptorExecutorService")public ExecutorService entranceAlerterThread() {ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNamePrefix("thread-mybatisInterceptorExecutorService-runner-%d").build();return new ThreadPoolExecutor(4, 36, 200L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(204800), namedThreadFactory);}}
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.lang.reflect.Proxy;
import java.util.Properties;/*** 插件工具类*/
public final class PluginUtils {public static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";private PluginUtils() {// to do nothing}/*** 获得真正的处理对象,可能多层代理.*/@SuppressWarnings("unchecked")public static <T> T realTarget(Object target) {if (Proxy.isProxyClass(target.getClass())) {MetaObject metaObject = SystemMetaObject.forObject(target);return realTarget(metaObject.getValue("h.target"));}return (T) target;}/*** 根据 key 获取 Properties 的值*/public static String getProperty(Properties properties, String key) {String value = properties.getProperty(key);return StringUtils.isBlank(value) ? null : value;}
}

数据权限控制

比如根据部门权限动态修改sql

import cn.hutool.core.util.ObjectUtil;
import com.disclosure.common.core.domain.entity.SysRole;
import com.disclosure.common.core.domain.entity.SysUser;
import com.disclosure.common.core.domain.model.LoginUser;
import com.disclosure.common.utils.SecurityUtils;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;/*** @author <p>* mybatis 数据权限拦截器*/
@Slf4j
@AllArgsConstructor
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare" , args = {Connection.class, Integer.class})})
public class AuthorityScopeInterceptor implements Interceptor {private final ExecutorService executorService;@Override@SneakyThrowspublic Object intercept(Invocation invocation) {StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());MetaObject metaObject = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");//指定范围拦截if (!mappedStatement.getId().startsWith("com.disclosure.business")) return invocation.proceed();//只拦截 三种类型if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())&& !SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())&& !SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType())) return invocation.proceed();//获取用户属性LoginUser loginUser = SecurityUtils.getLoginUser();SysUser currentUser;if (ObjectUtil.isNull(loginUser) || ObjectUtil.isNull(currentUser = loginUser.getUser()))throw new RuntimeException("获取不到用户数据");// 如果是超级管理员,则不过滤数据if (currentUser.isAdmin()) return invocation.proceed();List<SysRole> roles = currentUser.getRoles();if (roles.isEmpty()) throw new RuntimeException("角色缺失");AuthorityCategory authorityCategory = new AuthorityCategory(executorService);//获取多角色的权限范围for (SysRole role : roles) {switch (role.getDataScope()) {case AuthorityCategory.DATA_SCOPE_ALL:return invocation.proceed();case AuthorityCategory.DATA_SCOPE_CUSTOM:authorityCategory.addDataScopeCustom(role.getRoleId());break;case AuthorityCategory.DATA_SCOPE_DEPT:authorityCategory.addDataScopeDept(currentUser.getDeptId());break;case AuthorityCategory.DATA_SCOPE_DEPT_AND_CHILD:authorityCategory.addDataScopeDeptAndChild(currentUser.getDeptId());break;default:authorityCategory.addDataScopeSelf(currentUser.getUserId());break;}}authorityCategory.removeRepeatDept();BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");String originalSql = boundSql.getSql();//生成新sqloriginalSql = authorityCategory.getNewSql(originalSql).toString();metaObject.setValue("delegate.boundSql.sql" , originalSql);return invocation.proceed();}/*** 生成拦截对象的代理** @param target 目标对象* @return 代理对象*/@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}/*** mybatis配置的属性** @param properties mybatis配置的属性*/@Overridepublic void setProperties(Properties properties) {}
}
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.Data;
import lombok.SneakyThrows;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.statement.select.SelectBody;
import net.sf.jsqlparser.statement.update.Update;import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;import static com.disclosure.business.config.InitiateInterceptField.create_by;
import static com.disclosure.business.config.InitiateInterceptField.dept_id;@Data
public class AuthorityCategory {private final ExecutorService executorService;/*** 全部*/public static final String DATA_SCOPE_ALL = "1";/*** 自定数据权限*/public static final String DATA_SCOPE_CUSTOM = "2";/*** 部门数据权限*/public static final String DATA_SCOPE_DEPT = "3";/*** 部门及以下数据权限*/public static final String DATA_SCOPE_DEPT_AND_CHILD = "4";/*** 仅本人数据权限*/public static final String DATA_SCOPE_SELF = "5";private ArrayList<Long> dataScopeCustomList;private ArrayList<Long> dataScopeDeptList;private ArrayList<Long> dataScopeDeptAndChildList;private ArrayList<Long> dataScopeSelfList;private String fromItemName;public AuthorityCategory(ExecutorService executorService) {this.executorService = executorService;this.dataScopeCustomList = new ArrayList<>();this.dataScopeDeptList = new ArrayList<>();this.dataScopeDeptAndChildList = new ArrayList<>();this.dataScopeSelfList = new ArrayList<>();this.fromItemName = "";}public void addDataScopeSelf(Long dataScopeSelf) {this.dataScopeSelfList.add(dataScopeSelf);}public void addDataScopeDeptAndChild(Long dataScopeDeptAndChild) {this.dataScopeDeptAndChildList.add(dataScopeDeptAndChild);}public void addDataScopeDept(Long dataScopeDept) {this.dataScopeDeptList.add(dataScopeDept);}public void addDataScopeCustom(Long dataScopeCustom) {this.dataScopeCustomList.add(dataScopeCustom);}public void removeRepeatDept() {dataScopeDeptList.removeAll(dataScopeDeptAndChildList);}@SneakyThrowspublic Statement getNewSql(String originalSql) {//解析只针对主表Statement statement = CCJSqlParserUtil.parse(new StringReader(originalSql));if (statement instanceof Select) {Select select = (Select) statement;SelectBody selectBody = select.getSelectBody();PlainSelect plainSelect = (PlainSelect) selectBody;plainSelect.setWhere(spliceSQl(plainSelect.getWhere(), plainSelect.getFromItem()));return select;} else if (statement instanceof Delete) {Delete delete = (Delete) statement;delete.setWhere(spliceSQl(delete.getWhere(), delete.getTable()));return delete;} else if (statement instanceof Update) {Update update = (Update) statement;update.setWhere(spliceSQl(update.getWhere(), update.getTable()));return update;}return statement;}private Expression spliceSQl(Expression expression, FromItem fromItem) {List<Expression> expressionList = this.newWhere(fromItem);if (ObjectUtil.isNull(expression)) {return getOr(expressionList);} else {AndExpression andExpression = new AndExpression();andExpression.setLeftExpression(expression);andExpression.setRightExpression(new Column("(" + getOr(expressionList) + ")"));return expression;}}/*** 处理 四种权限类型*/@SneakyThrowsprivate List<Expression> newWhere(FromItem fromItem) {handleFromItemName(fromItem);Future<Expression> handleDataScopeSelfLisSubmit = executorService.submit(this::handleDataScopeSelfList);Future<Expression> handleDataScopeDeptListSubmit = executorService.submit(this::handleDataScopeDeptList);Future<Expression> handleDataScopeCustomListSubmit = executorService.submit(this::handleDataScopeCustomList);Future<Expression> handleDataScopeDeptAndChildListSubmit = executorService.submit(this::handleDataScopeDeptAndChildList);return Stream.of(handleDataScopeSelfLisSubmit.get(),handleDataScopeDeptListSubmit.get(),handleDataScopeCustomListSubmit.get(),handleDataScopeDeptAndChildListSubmit.get()).filter(ObjectUtil::isNotNull).collect(Collectors.toList());}/*** 获取名字或别名*/private void handleFromItemName(FromItem fromItem) {if (ObjectUtil.isNull(fromItem.getAlias())) {this.fromItemName = fromItem.toString();} else {this.fromItemName = fromItem.getAlias().getName();}}/*** 拼接仅本人数据权限sql*/private Expression handleDataScopeSelfList() {if (!this.dataScopeSelfList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), create_by));inExpression.setRightExpression(new Column("(" + CollUtil.join(this.dataScopeSelfList, ",") + ")"));return new Column("(" + inExpression + ")");}return null;}/*** 拼接仅部门数据权限sql*/private Expression handleDataScopeDeptList() {if (!this.dataScopeDeptList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), dept_id));inExpression.setRightExpression(new Column("(" + CollUtil.join(this.dataScopeDeptList, ",") + ")"));return new Column("(" + inExpression + ")");}return null;}/*** 拼接自定义数据权限sql*/private Expression handleDataScopeCustomList() {if (!this.dataScopeCustomList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), dept_id));inExpression.setRightExpression(new Column("(SELECT dept_id FROM sys_role_dept WHERE role_id in (" + CollUtil.join(this.dataScopeCustomList, ",") + "))"));return new Column("(" + inExpression + ")");}return null;}/*** 拼接部门及以下数据权限sql*/private Expression handleDataScopeDeptAndChildList() {if (!this.dataScopeDeptAndChildList.isEmpty()) {InExpression inExpression = new InExpression();inExpression.setLeftExpression(new Column(new Table(this.fromItemName), dept_id));inExpression.setRightExpression(new Column("(SELECT dept_id FROM sys_dept WHERE dept_id in (" + CollUtil.join(this.dataScopeDeptAndChildList, ",") + ") or " + this.dataScopeDeptAndChildList.parallelStream().map(n -> "find_in_set(" + n + ",ancestors)").collect(Collectors.joining(" or ")) + ")"));return new Column("(" + inExpression + ")");}return null;}private Expression getOr(List<Expression> expressions) {if (expressions.size() == 1) return expressions.get(0);String collect = expressions.parallelStream().map(Object::toString).collect(Collectors.joining(" or "));return new Column(collect);}}

入参修改

比如修改机构字段,删除机构字段,填充新建用户

import cn.hutool.core.util.ObjectUtil;
import com.disclosure.common.core.domain.entity.SysUser;
import com.disclosure.common.core.domain.model.LoginUser;
import com.disclosure.common.utils.SecurityUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.update.Update;
import net.sf.jsqlparser.statement.update.UpdateSet;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.io.StringReader;
import java.sql.Connection;
import java.util.*;
import java.util.stream.Collectors;import static com.disclosure.business.config.InitiateInterceptField.*;@Slf4j
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare" , args = {Connection.class, Integer.class})})
public class AuthorityUserFillerInterceptor implements Interceptor {//清理指定映射字段private final String[] deleteParameter = {deptId, createBy, updateBy};private final String[] deleteColumn = {dept_id, create_by, update_by};@Override@SneakyThrowspublic Object intercept(Invocation invocation) {StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());MetaObject metaObject = SystemMetaObject.forObject(statementHandler);MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");if (!mappedStatement.getId().startsWith("com.disclosure.business")) return invocation.proceed();if (!SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType())&& !SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {return invocation.proceed();}LoginUser loginUser = SecurityUtils.getLoginUser();SysUser currentUser;if (ObjectUtil.isNull(loginUser) || ObjectUtil.isNull(currentUser = loginUser.getUser()))throw new RuntimeException("获取不到用户数据");BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");String originalSql = boundSql.getSql();Statement statement = CCJSqlParserUtil.parse(new StringReader(originalSql));List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();//删除参数映射以免出现下列问题//        Parameter index out of range (X > number of parameters, which is X)List<String> deleteParameterList = Arrays.asList(deleteParameter);List<ParameterMapping> deptIdParameterMappings = parameterMappings.stream().parallel().map(n -> {if (deleteParameterList.contains(n.getProperty())) {return n;}return null;}).filter(ObjectUtil::isNotNull).collect(Collectors.toList());if (!deptIdParameterMappings.isEmpty()) {parameterMappings.removeAll(deptIdParameterMappings);}if (statement instanceof Update) {Update update = (Update) CCJSqlParserUtil.parse(originalSql);ArrayList<UpdateSet> updateSets = update.getUpdateSets();//删除sql对应值updateCleanse(updateSets);//填充信息Map<String, Object> updateParameter = new HashMap<String, Object>() {{put(update_by, currentUser.getUserId());}};setUpdateParameter(updateSets, updateParameter);originalSql = update.toString();} else if (statement instanceof Insert) {Insert insert = (Insert) CCJSqlParserUtil.parse(originalSql);List<Column> columns = insert.getColumns();List<Expression> expressions = insert.getItemsList(ExpressionList.class).getExpressions();insertCleanse(columns, expressions);Map<String, Object> insertParameter = new HashMap<String, Object>() {{put(dept_id, currentUser.getDeptId());put(create_by, currentUser.getUserId());}};//填充信息setInsertParameter(columns, expressions, insertParameter);originalSql = insert.toString();} else {return invocation.proceed();}metaObject.setValue("delegate.boundSql.sql" , originalSql);return invocation.proceed();}/*** 填充Insert入参,只做string和long,可另行扩展*/private void setInsertParameter(List<Column> columns, List<Expression> expressions, Map<String, Object> insertParameter) {insertParameter.forEach((k, v) -> {columns.add(new Column(k));if (v instanceof Long) {expressions.add(new LongValue((long) v));} else {expressions.add(new StringValue((String) v));}});}/*** 填充update入参,只做string和long,可另行扩展*/public void setUpdateParameter(List<UpdateSet> updateSets, Map<String, Object> updateParameter) {updateParameter.forEach((k, v) -> {UpdateSet updateSet = new UpdateSet();if (v instanceof Long) {updateSet.add(new Column(k), new LongValue((long) v));} else {updateSet.add(new Column(k), new StringValue(String.valueOf(v)));}updateSets.add(updateSet);});}/*** 删除update数据*/private void updateCleanse(ArrayList<UpdateSet> updateSets) {List<String> deleteColumnList = Arrays.asList(deleteColumn);List<UpdateSet> deleteUpdateSets = updateSets.parallelStream().map(n -> {ArrayList<Column> columns = n.getColumns();for (Column column : columns) {if (deleteColumnList.contains(column.toString())) {return n;}}return null;}).filter(ObjectUtil::isNotNull).collect(Collectors.toList());if (!deleteUpdateSets.isEmpty()) {updateSets.removeAll(deleteUpdateSets);}}/*** 删除insert数据*/private void insertCleanse(List<Column> columns, List<Expression> expressions) {List<String> deleteColumnList = Arrays.asList(deleteColumn);//删除sql对应值List<Column> deleteColumn = columns.parallelStream().map(n -> {if (deleteColumnList.contains(n.toString())) {return n;}return null;}).filter(ObjectUtil::isNotNull).collect(Collectors.toList());if (!deleteColumn.isEmpty()) {deleteColumn.parallelStream().forEach(n -> expressions.remove(columns.indexOf(n)));columns.removeAll(deleteColumn);}}/*** 生成拦截对象的代理** @param target 目标对象* @return 代理对象*/@Overridepublic Object plugin(Object target) {if (target instanceof StatementHandler) {return Plugin.wrap(target, this);}return target;}/*** mybatis配置的属性** @param properties mybatis配置的属性*/@Overridepublic void setProperties(Properties properties) {}
}

返回数据修改

比如根据部门id查询部门返回部门名称

import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.ApiModel;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.*;import static com.disclosure.business.config.InitiateInterceptField.*;@Intercepts({@Signature(type = ResultSetHandler.class, method = "handleResultSets" , args = {Statement.class})})
public class FillUserInterceptor implements Interceptor {private final String[] initiateInterceptField = {deptId, createBy, updateBy};public Object intercept(Invocation invocation) throws Throwable {MetaObject metaStatementHandler = SystemMetaObject.forObject(invocation.getTarget());MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("mappedStatement");if (!mappedStatement.getId().startsWith("com.disclosure.business")) return invocation.proceed();if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) return invocation.proceed();List<ResultMap> resultMaps = mappedStatement.getResultMaps();Class<?> resultType = resultMaps.get(0).getType();//稍微判断一下只有实体类又这个注解才进入下面if (!resultType.isAnnotationPresent(ApiModel.class)) {return invocation.proceed();}//获取mybatis返回的实体类类型名int resultMapCount = resultMaps.size();if (resultMapCount > 0) {Statement statement = (Statement) invocation.getArgs()[0];ResultSet resultSet = statement.getResultSet();if (resultSet != null) {//获得对应列名ResultSetMetaData resultSetMetaData = resultSet.getMetaData();List<String> columnList = new ArrayList<>();List<String> initiateInterceptFieldList = Arrays.asList(initiateInterceptField);for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {//做下划线转驼峰操作columnList.add(StrUtil.toCamelCase(resultSetMetaData.getColumnName(i)));}initiateInterceptFieldList.retainAll(columnList);List<LinkedHashMap<String, Object>> resList = new ArrayList<>();while (resultSet.next()) {LinkedHashMap<String, Object> resultMap = new LinkedHashMap<>();for (String colName : columnList) {resultMap.put(colName, resultSet.getString(StrUtil.toUnderlineCase(colName)));}
//                    将转换后的map转换为实体类中,适用于实体类中有需要填充的数据
//                    Object o = resultType.newInstance();
//                    BeanUtils.populate(o, resultMap);resultMap.put(deptName, "测试部门");resultMap.put(createName, "测试新增人员");resultMap.put(updateName, "测试修改人员");resList.add(resultMap);}return resList;}}return invocation.proceed();}public Object plugin(Object target) {
//        System.out.println("将要包装的目标对象:" + target);return Plugin.wrap(target, this);}public void setProperties(Properties properties) {}
}

相关文章:

java 对mybatis拦截Interceptor进行权限控制(条件修改),入参修改,返回修改

使用jsqlparser对sql解析拼装 基础准备 <dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>4.5</version><scope>compile</scope> </dependency> <depende…...

鲲鹏+麒麟v10 sp1 打包tomcat

一、安装打包软件 yum install rpm-build yum install rpmdevtools 二、复制tar包 注意&#xff1a;如果要修改配置文件需要把tar包解压&#xff0c;修改tomcat配置文件 cp apache-tomcat-8.5.93.tar.gz /root/rpm-build/SOURCES 三、编辑/root/rpm-build/SOURCES/tomcat.…...

大屏大概是怎么个开发法(前端)

写在前面&#xff0c;博主是个在北京打拼的码农&#xff0c;从事前端工作5年了&#xff0c;做过十多个大大小小不同类型的项目&#xff0c;最近心血来潮在这儿写点东西&#xff0c;欢迎大家多多指教。 对于文章中出现的任何错误请大家批评指出&#xff0c;一定及时修改。有任何…...

什么是Selenium?使用Selenium进行自动化测试!

你知道什么是 Selenium 吗&#xff1f;你知道为什么要使用它吗&#xff1f;答案就在本文中&#xff0c;很高兴能够与你共飧。 自动化测试正席卷全球&#xff0c;Selenium 认证是业界最抢手的技能之一。 什么是 Selenium&#xff1f; Selenium 是一种开源工具&#xff0c;用于…...

第一章:最新版零基础学习 PYTHON 教程(第十七节 - Python 表达式语句–Python返回语句)

return 语句用于结束函数调用的执行,并将结果(return 关键字后面的表达式的值)“返回”给调用者。return 语句之后的语句不被执行。如果return语句没有任何表达式,则返回特殊值None。return 语句总体上用于调用函数,以便可以执行传递的语句。 注意: Return 语句不能在函数…...

前端碎知识点

1 统计字符串中每个字符出现的频率 const str fgasdfadfdasd;const result str.split().reduce((a, b) > {if (a[b]) {a[b];} else {a[b] 1;}return a; }, {});const resultNew str.split().reduce((a, b) > {// undefined&#xff0c;就是NaNa[b] || (a[b] 1);ret…...

【Leetcode Sheet】Weekly Practice 7

Leetcode Test 1462 课程表Ⅳ(9.12) 你总共需要上 numCourses 门课&#xff0c;课程编号依次为 0 到 numCourses-1 。你会得到一个数组 prerequisite &#xff0c;其中 prerequisites[i] [ai, bi] 表示如果你想选 bi 课程&#xff0c;你 必须 先选 ai 课程。 有的课会有直接…...

leetcode Top100(23)回文链表

给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true采用动态数组&#xff0c;判断数组对称就可以了&#xff08;这解法空间…...

WebGL绘制圆形的点

目录 前言 如何实现圆形的点&#xff1f; 片元着色器内置变量&#xff08;gl_FragCoord、gl_PointCoord&#xff09; gl_PointCoord的含义 示例程序&#xff08;RoundedPoint.js&#xff09; 代码详解 前言 本文将讨论示例程序RoundedPoint&#xff0c;该程序绘制了圆…...

《The Rise and Potential of Large Language Model Based Agents: A Survey》全文翻译

The Rise and Potential of Large Language Model Based Agents: A Surve - 基于 LLMs 的代理的兴起和潜力&#xff1a;一项调查 论文信息摘要1. 介绍2. 背景2.1 AI 代理的起源2.2 代理研究的技术趋势2.3 为什么大语言模型适合作为代理大脑的主要组件 论文信息 题目&#xff1…...

在线地图获取城市路网数据

在线地图获取城市路网数据 近期科研项目中&#xff0c;需要获取城市路网数据&#xff0c;于是详细阅读各大在线地图api接口&#xff0c;总结出来这么一条可行的思路&#xff1a; 首先获取城市轮廓根据城市轮廓把城市分割成若干个小块在每个小块中根据在线地图的POI检索接口&a…...

8.2 Jmeter if控制器使用

前提&#xff1a;jmeter脚本需要用到if控制器&#xff0c;if判断如果查询不到&#xff0c;则去新增。 1、添加if控制器 线程组-->逻辑控制器-->如果(if)控制器 1&#xff09;、Expression (must evaluate to true or false) &#xff1a;表达式&#xff08;值必须是tru…...

科技云报道:青云科技打出“AI算力牌”,抢跑“云+AI”新增市场

科技云报道原创。 近三年&#xff0c;中国云计算市场在多个维度同时发生着剧烈变化——疫情极大加速了全社会对于数字化的认知和接受程度&#xff1b;一系列云原生技术依托着开源和蓬勃的市场而迅速发展演变&#xff0c;更多产品和技术名词同时涌向市场&#xff1b;国际关系复…...

学习路之PHP--lumen安装配置

一、下载lumen源码 composer create-project --prefer-dist laravel/lumen blog 安装lumen-generator composer require flipbox/lumen-generator 二、配置 bootstrap\app.php 97行 $app->register(Flipbox\LumenGenerator\LumenGeneratorServiceProvider::class);三、生成…...

【C++】构造函数和析构函数第一部分(构造函数和析构函数的作用)--- 2023.9.25

目录 前言初始化和清理的概念构造函数和析构函数的作用构造函数的作用析构函数的作用 使用构造函数和析构函数的注意事项默认的构造函数和析构函数结束语 前言 在使用c语言开发的项目场景中&#xff0c;我们往往会遇到申请空间的需求&#xff0c;同时也肯定遇到过程序运行一段…...

CocosCreator3.8研究笔记(二十一)CocosCreator Tween系统理解

在 Cocos Creator 3.x 版本中&#xff0c; Tween系统代替了原来的Action系统。很多朋友不明白Tween到底是什么&#xff0c;Tween原理是什么&#xff1f;怎么使用Tween&#xff1f; 今天就来详细了解一下&#xff0c;希望能帮助到大家加深对Tween的了解&#xff0c;并快速掌握Tw…...

大数据学习-目录

学习内容持续更新ing 1.大数据学习1.0-Centos8虚拟机安装 大数据学习1.0-Centos8虚拟机安装_汉卿HanQ的博客-CSDN博客 2.大数据学习1.1-Centos8网络配置 大数据学习1.1-Centos8网络配置_汉卿HanQ的博客-CSDN博客 3.大数据学习1.2-yum配置 大数据学习1.2-yum配置_汉卿HanQ的…...

《动手学深度学习 Pytorch版》 7.5 批量规范化

7.5.1 训练深层网络 训练神经网络的实际问题&#xff1a; 数据预处理的方式会对最终结果产生巨大影响。 训练时&#xff0c;多层感知机的中间层变量可能具有更广的变化范围。 更深层的网络很复杂容易过拟合。 批量规范化对小批量的大小有要求&#xff0c;只有批量大小足够…...

Toaster - Android 吐司框架,专治 Toast 各种疑难杂症

官网 https://github.com/getActivity/Toaster 这可能是性能优、使用简单&#xff0c;支持自定义&#xff0c;不需要通知栏权限的吐司 想了解实现原理的可以点击此链接查看&#xff1a;Toaster 源码 集成步骤 如果你的项目 Gradle 配置是在 7.0 以下&#xff0c;需要在 bui…...

2023年9月26日,历史上的今天大事件早读

1620年9月26日大明皇帝朱常洛驾崩 1815年9月26日俄、普、奥三国在巴黎发表缔结“神圣同盟” 1841年9月26日清代思想家、诗人龚自珍逝世 1849年9月26日“生理学之父”巴甫洛夫诞生 1909年9月26日云南陆军讲武堂创办 1953年9月26日画家徐悲鸿逝世 1980年9月26日国际宇航联合…...

浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)

✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义&#xff08;Task Definition&…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录 &#x1f50d; 若用递归计算每一项&#xff0c;会发生什么&#xff1f; Horners Rule&#xff08;霍纳法则&#xff09; 第一步&#xff1a;我们从最原始的泰勒公式出发 第二步&#xff1a;从形式上重新观察展开式 &#x1f31f; 第三步&#xff1a;引出霍纳法则&…...

StarRocks 全面向量化执行引擎深度解析

StarRocks 全面向量化执行引擎深度解析 StarRocks 的向量化执行引擎是其高性能的核心设计&#xff0c;相比传统行式处理引擎&#xff08;如MySQL&#xff09;&#xff0c;性能可提升 5-10倍。以下是分层拆解&#xff1a; 1. 向量化 vs 传统行式处理 维度行式处理向量化处理数…...

VASP软件在第一性原理计算中的应用-测试GO

VASP软件在第一性原理计算中的应用 VASP是由维也纳大学Hafner小组开发的一款功能强大的第一性原理计算软件&#xff0c;广泛应用于材料科学、凝聚态物理、化学和纳米技术等领域。 VASP的核心功能与应用 1. 电子结构计算 VASP最突出的功能是进行高精度的电子结构计算&#xff…...