前后端配合实现按钮级操作权限控制

news2025/1/18 7:42:09

背景

公司项目需要做到按钮级权限限制,至此有了该文,如有错误,请联系博主指出,多多感谢。

角色配置前后端操作

首先最基本的角色配置,配置该类角色有哪些菜单以及那些菜单的哪些按钮权限

菜单及菜单按钮由前端维护(或者也可以后端数据库维护)

前端维护一个JSON文件,直接读取渲染页面即可

JSON文件类似这样,定义菜单及菜单下按钮,声明唯一key和name(角色配置时需要存储对应菜单及按钮的key)

[
  {
    key: 'Home',
    menu: '首页',
  },
  {
    key: 'OrgManagement',
    menu: '组织管理',
    children: [
      {
        key: 'RoleConfig',
        menu: '角色信息配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'PersonnelConfig',
        menu: '人员信息配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'TeamsConfig',
        menu: '班组信息配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
    ],
  },
  {
    key: 'FacilityManagement',
    menu: '设施管理',
    children: [
      {
        key: 'LineManagement',
        menu: '线体管理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'DeviceManagement',
        menu: '设备管理',
        children: [
          {
            key: 'DeviceManagement_DeviceType',
            menu: '设备类型',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
          {
            key: 'DeviceManagement_Device',
            menu: '设备',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
        ],
      },
      {
        key: 'ComponentManagement',
        menu: '部件管理',
        children: [
          {
            key: 'ComponentManagement_ComponentType',
            menu: '部件类型',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
          {
            key: 'ComponentManagement_Component',
            menu: '部件',
            disableSelect: true,
            checkPermissions: [],
            permissionOptions: [
              {
                key: 'add',
                name: '新增',
              },
              {
                key: 'edit',
                name: '编辑',
              },
              {
                key: 'delete',
                name: '删除',
              },
            ],
          },
        ],
      },
    ],
  },
  {
    key: 'OamAlarm',
    menu: '运维报警',
    children: [
      {
        key: 'AlarmTemplate',
        menu: '报警模版',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'AlarmRecords',
        menu: '报警明细',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'ignore',
            name: '忽略',
          },
          {
            key: 'createOrder',
            name: '生成工单',
          },
          {
            key: 'oneKeyHandle',
            name: '一键处理',
          },
        ],
      },
      {
        key: 'AlarmWorkOrderRecords',
        menu: '工单明细',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'receive',
            name: '接单',
          },
        ],
      },
      {
        key: 'AlarmLevel',
        menu: '报警等级配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
    ],
  },
  {
    key: 'OamPatrol',
    menu: '运维巡检',
    children: [
      {
        key: 'PatrolTaskConfig',
        menu: '巡检任务配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'FrequencyRulesConfig',
        menu: '频率规则配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'DistributeRulesConfig',
        menu: '下发规则配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'PatrolTaskManagement',
        menu: '巡检任务处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'distribute',
            name: '下发',
          },
          {
            key: 'handle',
            name: '处理',
          },
        ],
      },
    ],
  },
  {
    key: 'Maintenance',
    menu: '维修保养',
    children: [
      {
        key: 'MaintenanceDeviceConfig',
        menu: '维保设备配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'MaintenancePlanConfig',
        menu: '维保计划配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'MaintenanceRemindConfig',
        menu: '维保提醒配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'MaintenanceTaskManagement',
        menu: '维保任务处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'distribute',
            name: '下发',
          },
        ],
      },
    ],
  },
  {
    key: 'SpareParts',
    menu: '备品备件',
    children: [
      {
        key: 'WarehouseConfig',
        menu: '库房库位配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'SpareTypeConfig',
        menu: '备件类型配置',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },
      {
        key: 'SpareAccountManagement',
        menu: '备件台帐管理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
        ],
      },

      {
        key: 'PurchaseApply',
        menu: '采购申请',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'examine',
            name: '审核',
          },
          {
            key: 'createPurchaseOrder',
            name: '生成采购单',
          },
        ],
      },

      {
        key: 'ArrivalManagement',
        menu: '到货处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'confirm',
            name: '到货确认',
          },
          {
            key: 'entry',
            name: '生成入库',
          },
        ],
      },

      {
        key: 'EntryStorageManagement',
        menu: '入库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'entry',
            name: '入库',
          },
        ],
      },

      {
        key: 'ExitStorageApply',
        menu: '出库申请',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'examine',
            name: '审核',
          },
        ],
      },

      {
        key: 'ExitStorageManagement',
        menu: '出库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'exit',
            name: '出库',
          },
        ],
      },

      {
        key: 'ExitEntryStorageRecords',
        menu: '出入库记录',
      },

      {
        key: 'StocktakingManagement',
        menu: '盘库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'stocktaking',
            name: '盘库',
          },
        ],
      },

      {
        key: 'TransferManagement',
        menu: '调库处理',
        checkPermissions: [],
        permissionOptions: [
          {
            key: 'add',
            name: '新增',
          },
          {
            key: 'edit',
            name: '编辑',
          },
          {
            key: 'delete',
            name: '删除',
          },
          {
            key: 'handle',
            name: '处理',
          },
        ],
      },
    ],
  },
  {
    key: 'Dict',
    menu: '数据字典',
  },
  {
    key: 'Knowledge',
    menu: '知识库',
    checkPermissions: [],
    permissionOptions: [
      {
        key: 'add',
        name: '新增',
      },
      {
        key: 'edit',
        name: '编辑',
      },
      {
        key: 'delete',
        name: '删除',
      },
    ],
  },
]

根据上述JSON文件,渲染出的角色权限编辑页面如下:

image-20231031125412416

该页面可以配置角色菜单及按钮权限,前端请求参数如下:

{
  "roleName": "库管员",
  "roleSort": 1,
  "remark": "库管员",
  "menuIds": [
    "SpareParts",
    "WarehouseConfig",
    "SpareTypeConfig",
    "SpareAccountManagement",
    "PurchaseApply",
    "ArrivalManagement",
    "EntryStorageManagement",
    "ExitStorageApply",
    "ExitStorageManagement",
    "ExitEntryStorageRecords",
    "StocktakingManagement",
    "TransferManagement"
  ],
  "permissions": [
    "WarehouseConfig/add",
    "WarehouseConfig/edit",
    "WarehouseConfig/delete",
    "SpareTypeConfig/add",
    "SpareTypeConfig/edit",
    "SpareTypeConfig/delete",
    "SpareAccountManagement/add",
    "SpareAccountManagement/edit",
    "SpareAccountManagement/delete",
    "PurchaseApply/add",
    "PurchaseApply/edit",
    "PurchaseApply/delete",
    "PurchaseApply/examine",
    "PurchaseApply/createPurchaseOrder",
    "ArrivalManagement/confirm",
    "ArrivalManagement/entry",
    "EntryStorageManagement/add",
    "EntryStorageManagement/edit",
    "EntryStorageManagement/delete",
    "EntryStorageManagement/entry",
    "ExitStorageApply/add",
    "ExitStorageApply/edit",
    "ExitStorageApply/delete",
    "ExitStorageApply/examine",
    "ExitStorageManagement/add",
    "ExitStorageManagement/delete",
    "ExitStorageManagement/exit",
    "StocktakingManagement/add",
    "StocktakingManagement/edit",
    "StocktakingManagement/delete",
    "StocktakingManagement/stocktaking",
    "TransferManagement/add",
    "TransferManagement/edit",
    "TransferManagement/delete",
    "TransferManagement/handle"
  ]
}

后端需要将该角色有的菜单权限及按钮权限存起来,存在sys_role表中,表结构如下:

image-20231031130430532

主要关注划红线的两个字段,将上述请求参数,分别用逗号隔开存与这两个字段(菜单以及按钮权限)中,此时角色菜单级及按钮级权限以维护好!(此时可以创建有不同操作权限的角色了)

新增用户时,就可以直接选择对应角色,维护好用户和角色的对应关系,用户就有了对应角色的权限了(用户登录后,查询用户对应菜单权限以及按钮权限,前端可以直接根据返回权限展示菜单及按钮权限【此时连菜单权限也一并做了】)。

其实到这里基本就不会有什么问题了,不同角色用户,只能操作自己有的权限操作。

但是以防万一,后端对应接口加个校验会更加安全。

后端权限校验

注解校验

因为后端用的Java的springBoot框架,可以很方便的进行aop操作。使用注解来实现鉴权操作。

注解如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface RequiresPermissions
{
    /**
     * 需要校验的权限码
     */
    String[] value() default {};

    /**
     * 验证模式:AND | OR,默认AND
     *(这个属性对应场景如下:
     * 前端有多个按钮操作可能是调用一个接口/
     * 后端新增或者编辑定义的是一个接口)
     * 前者属性用 OR,后者用 AND 【权限会存在 and/or 的关系】
     */
    Logical logical() default Logical.AND;
}

aop切面类

@Aspect
@Component
public class PreAuthorizeAspect
{
    /**
     * 构建
     */
    public PreAuthorizeAspect()
    {}

    /**
     * 定义AOP签名 (切入所有使用鉴权注解的方法)【切点】
     */
    public static final String POINTCUT_SIGN = " @annotation(com.smart.common.security.annotation.RequiresLogin) || "
            + "@annotation(com.smart.common.security.annotation.RequiresPermissions) || "
            + "@annotation(com.smart.common.security.annotation.RequiresRoles)";

    /**
     * 声明AOP签名
     */
    @Pointcut(POINTCUT_SIGN)
    public void pointcut()
    {}

    /**
     * 环绕切入
     * 
     * @param joinPoint 切面对象
     * @return 底层方法执行后的返回值
     * @throws Throwable 底层方法抛出的异常
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable
    {
        // 注解鉴权
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        checkMethodAnnotation(signature.getMethod());
        try
        {
            // 执行原有逻辑
            Object obj = joinPoint.proceed();
            return obj;
        }
        catch (Throwable e)
        {
            throw e;
        }
    }

    /**
     * 对一个Method对象进行注解检查
     */
    public void checkMethodAnnotation(Method method) {
        // 校验 @RequiresLogin 注解
        RequiresLogin requiresLogin = method.getAnnotation(RequiresLogin.class);
        if (requiresLogin != null) {
            AuthUtil.checkLogin();
        }

        // 校验 @RequiresRoles 注解
        RequiresRoles requiresRoles = method.getAnnotation(RequiresRoles.class);
        if (requiresRoles != null) {
            AuthUtil.checkRole(requiresRoles);
        }

        // 校验 @RequiresPermissions 注解 【主要看这个校验逻辑】
        RequiresPermissions requiresPermissions = method.getAnnotation(RequiresPermissions.class);
        if (requiresPermissions != null) {
            AuthUtil.checkPermi(requiresPermissions);
        }
    }
}

核心处理逻辑在这AuthUtil.checkPermi

    public void checkPermi(RequiresPermissions requiresPermissions) {
	SecurityContextHolder.setPermission(StringUtils.join(requiresPermissions.value(), ","));
		// 判断是否有所有权限或者是单个权限就可以
        if (requiresPermissions.logical() == Logical.AND) {
            checkPermiAnd(requiresPermissions.value());
        }
        else {
            checkPermiOr(requiresPermissions.value());
        }
    }

checkPermiAnd 权限与逻辑,所有权限满足才能调用该方法

    public void checkPermiAnd(String... permissions) {
        Set<String> permissionList = getPermiList();
        for (String permission : permissions) {
            if (!hasPermi(permissionList, permission)) {
                throw new NotPermissionException(permission);
            }
        }
    }

hasPermi逐个判断是否有对应操作权限,只要一个不满足直接抛异常。

checkPermiOr 权限或逻辑,只要有权限满足就能直接调用该方法

    public void checkPermiOr(String... permissions) {
        // 登录后将用户信息存在ThreadLocal中,直接获取登录用户操作权限列表
        Set<String> permissionList = getPermiList();
        for (String permission : permissions) {
            if (hasPermi(permissionList, permission)) {
                return;
            }
        }
        if (permissions.length > 0) {
            throw new NotPermissionException(permissions);
        }
    }
    // 所有权限标识
	private static final String ALL_PERMISSION = "*:*:*";

	// 【or 逻辑】走到这里,只要一个满足条件直接结束,鉴权结束,不会抛异常
	public boolean hasPermi(Collection<String> authorities, String permission) {
        // 前半段校验是否有所有权限,后半段匹配当前权限是否在当前用户权限中
        return authorities.stream().filter(StringUtils::hasText)
                .anyMatch(x -> ALL_PERMISSION.contains(x) || PatternMatchUtils.simpleMatch(x, permission));
    }

接口使用的话就比较简单了,直接加个注解添加最开始前端传给后端保存的操作标识即可,如下:

image-20231031154546860

这下前后端双保险,按钮级操作权限到此已经实现。撒花✿✿ヽ(°▽°)ノ✿

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1154068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue Router使用VueUse更改标签页名称的工具函数

进入正题 安装 npm i vueuse/core or pnpm i vueuse/core or yarn add vueuse/corerouter/helper.js import { useTitle } from vueuse/coreexport const usePageTitle (to) > {const projectTitle import.meta.env.VITE_APP_TITLE // 将可变名抽出到 .env 内配置cons…

源码与SaaS:企业家如何选择?——一语道破真相

在数字化的时代&#xff0c;软件技术已经成为企业运营的核心驱动力。对于企业家来说&#xff0c;选择一个适合自己企业的软件开发方式至关重要。其中&#xff0c;源码和SaaS是两种常见的选择。那么&#xff0c;在这两者之间&#xff0c;企业家应该如何抉择&#xff1f; 源码&am…

excel修改日期格式为yyyy-mm-dd

1、选中要修改日期格式的列&#xff0c;鼠标右击&#xff0c;选择“设置单元格格式” 2、若“日期”格式中没有需要的格式&#xff0c;选择自定义格式&#xff0c;输入自己需要的日期格式后点击确定即可修改

简单又有效!制作产品说明书的模板工具

产品说明书在产品推广中起着至关重要的作用。它是一种不可忽视的宣传手段&#xff0c;能够向潜在客户传达产品的特点和优势。通过详细描述产品的功能、用途和特点&#xff0c;产品说明书能够帮助客户更好地了解产品&#xff0c;并激发他们对产品的兴趣。因此&#xff0c;在进行…

Mysql权限控制语句

1.创建用户 create user ky32localhost IDENTIFIED by 123456 create user&#xff1a;创建用户开头 ky32&#xff1a;用户名 localhost 新建的用户可以在哪些主机上登录 即可以使用ip地址&#xff0c;网段主机名 ky32localhost ky32192.168.233.22 ky32192.168.233.0/2…

SUE3000 1VCF750090R804 REM615面板

SUE3000 1VCF750090R804 REM615面板 蓝色波长激光的特殊特性使扫描仪适用于各种材料的高精度轮廓和尺寸测量&#xff0c;包括闪亮的表面、炽热的发光金属、有机材料(如食品、木材和木质单板)&#xff0c;以及透明或半透明材料&#xff0c;如塑料、玻璃、光学元件和薄膜/基底。…

DNS服务器使用_windows篇

1-搭建dns服务器 安装涉及内容&#xff1a;1-安装DNS服务器&#xff1b;2-DNS正向解析&#xff1b;3-DNS反向解析&#xff1b;4-DNS转发器&#xff1b;5-主、辅域名服务器&#xff1b;6-DNS子域委派服务器 二、使用方面 1-新建域 2-新建主机 三、其它内容见附件&#xff…

Playwright测试自动化工具

作者观点&#xff1a;很长时间以来&#xff0c;Selenium是QA工程师寻求测试自动化解决方案的首选测试框架。它能够测试任何浏览器&#xff08;这在IE浏览器的统治时期尤其重要&#xff09;和任何平台。然而&#xff0c;现在看来&#xff0c;那个时代已经过去了。 今天&#xf…

修复缺失d3dcompiler_43.dll问题的多种解决方案,2分钟解决d3dcompiler_43.dll文件

你是否曾遇到这样的提示&#xff1a;“程序无法启动&#xff0c;因为您的计算机上缺少d3dcompiler_43.dll”? 如果是的话&#xff0c;不用担心。 这篇文章将提供详细的步骤解决缺失d3dcompiler_43.dll的问题&#xff0c;教你怎么一步步的解决d3dcompiler_43.dll的缺失问题。 什…

SpringBoot+MINIO

Linux安装MINIO https://blog.csdn.net/tongxin_tongmeng/article/details/133934115 MINIO创建桶MINIO创建秘钥MINIO的API路径 http://your-server-ip:9000 注意&#xff1a;API路径在日志文件中/opt/minio/minio.log pom.xml <!-- https://mvnrepository.com/artifact/com…

通达信换手率指标公式,衡量股票的活跃程度

换手率是指在一定时间内股票的交易量与流通股本的比率&#xff0c;反映了股票的流动性和市场交易活跃程度。换手率高意味着股票的交易频繁&#xff0c;市场上的买卖力量较强&#xff0c;反之换手率低则表示交易相对较少。高换手率可能意味着投资者对该股票有较大的兴趣或者存在…

多路IO—POll函数,epoll服务器开发流程

引言 "在计算机网络编程中&#xff0c;多路IO技术是非常常见的一种技术。其中&#xff0c;Poll函数和Epoll函数是最为常用的两种多路IO技术。这两种技术可以帮助服务器端处理多个客户端的并发请求&#xff0c;提高了服务器的性能。本文将介绍Poll和Epoll函数的使用方法&am…

asp.net旅游交流管理信息系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net 旅游交流管理信息系统是一套完善的web设计管理系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使用c# 语言开发 asp.net旅游交流网站1 应用技…

【脑机接口 论文】利用脑机接口帮助ALS患者恢复对家用设备的控制science

英文题目 中文题目 稳定的语音BCI解码使ALS患者在3个月内无需重新校准即可进行控制论文下载&#xff1a;算法程序下载&#xff1a;摘要1 项目介绍2 方法2.1实时神经解码2.2算法手术植入:神经解码模型: 数据收集实验2.3稳定的解码器性能超过三个月 3 电极的贡献4 讨论5结论 中文…

Linux shell编程学习笔记18:while循环语句

上回我们研究和探讨了Linux shell编程中for 循环语句&#xff0c;与在C/C中一样&#xff0c;for 循环语句Linux shell编程中有很多灵活的用法。今天我们来研究和探讨while循环语句。 一、数字条件循环 我们继续以for循环语句中的例子&#xff0c;计算 从1到10与2的乘积 并输出…

实时定位和配送追踪:开发万岳同城外卖APP的关键技术特性

随着生活节奏的不断加快&#xff0c;外卖服务已经成为许多人日常生活中不可或缺的一部分。无论是工作日的午餐&#xff0c;还是周末的家庭聚会&#xff0c;外卖APP已经成为满足各种美食需求的首选方式。然而&#xff0c;同城外卖APP的成功不仅仅取决于美味的食物选择&#xff0…

python 打印与去除不可见字符 \x00

# 此处不是真实的\x00 被 空格替换了 text "boot_1__normal/ " print(text.strip()"boot_1__normal/") # 打印不可见字符 print(repr(text))>>> False boot_1__normal/\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0…

[HGAME 2023 week1]Classic Childhood Game js前端查看

记录一下 给了我们一个游戏 按照web的尿性 都是要通关或者干嘛才可以 我们现在去看看js 这里发现结局的函数 window.setTimeout(function () {Message ["[Hero]玩家&#xff0c;恭喜你&#xff01;通关普通结局的纪元魔塔。", "[Npc3,仙子]谢谢支持&#xff…

私募机构全球化视野探析 | 活动回顾

10月21日&#xff0c;华锐技术、国泰君安证券和亚马逊云科技在深圳联合举办了一场主题为“私募机构全球化视野探析”的交流活动&#xff0c;广深及周边地区数十家机构近百人参会。本次活动众多行业大咖和专家从服务、技术、数据、资金等多个角度&#xff0c;共同探讨私募机构全…