SpringBoot3 JDK21 Vue3开源后台RBAC管理系统 | 2024年好用的开源RBAC管理系统 | 数据权限的探索

序言

项目现已全面开源,商业用途完全免费

当前版本:v0.7.2

如果喜欢这个项目或支持作者,欢迎Star、Fork、Watch
一键三连 🚀
!!

在构建此代码框架的过程中,我已投入了大量精力,力求使其功能完善、结构清晰。然而,鉴于个人技术水平和经验的限制,框架中可能存在一些问题或可以改进的地方。对于任何可能的缺陷或不足,我在此表示诚挚的歉意,并恳请各位给予谅解。 我非常期待收到您的反馈和建议,您的每一条意见都是帮助我改进框架、提升技术水平的宝贵资源。让我们共同协作,不断优化和完善这个框架。 感谢您的使用与支持,期待与您的交流和合作!

简介

接触了很多优秀的开源和闭源项目,在使用过程中也发现一些问题,不甘满足的我遂产生了想法:于是利用休息时间编写了一套后台管理系统,它灵活、简洁、高效,拥抱最新的技术,因此Sz-Admin便诞生了,也意为升职Admin,升职加薪节节高。

Sz Admin ,一个基于 Spring Boot 3、Vue 3 和 Element-Plus 的开源中后台管理框架,致力于为您提供一个流畅、直观且功能强大的开发框架。它不仅融合了最新的技术趋势,而且通过精心设计,确保了系统的简洁性和高效,让使用者可以专注业务。

在这里插入图片描述

在线体验

  • 官网地址:https://szadmin.cn

数据权限

在数据权限构建的这场复杂而深刻的旅程中,作者经过了深思熟虑和不懈的探索。我们在这里展示的解决方案,虽然可能不是完美无瑕,但它代表了我们对问题的一种理解和尝试。我们希望这不仅仅是一个方案的展示,更是一个思考的起点。

同时,作者在实现数据权限的过程中,不禁提出了一个关键问题:“我们是否真的需要构建如此复杂的数据权限体系?这样的复杂性是否真正为我们带来了价值?” 。 我们鼓励每一位用户,带着这样的问题,来体验此功能。

目录

    • 序言
    • 简介
    • 在线体验
  • 数据权限
  • 一级目录
    • 二级目录
      • 三级目录
    • 读前须知
      • 注意事项
      • 内置规则
    • 实现原理
      • 数据结构
      • UML时序
      • 核心逻辑
        • 用户登录与权限规则预处理
          • 非自定义权限配置处理
          • 自定义权限配置处理
          • 合并规则细则
          • RuleMap 权限规则映射
          • 预处理Session数据结构与示例
      • 数据权限控制实现
    • 使用指南
      • 准备工作
        • 步骤一:新建菜单与设置权限
        • 步骤二:SQL层添加权限标识
        • 步骤三:设置数据权限角色
        • 步骤四:关联用户与数据权限角色
        • 步骤五:测试验证
    • 效果展示
      • 账户与权限配置
      • 权限效果验证
      • 操作步骤
      • 预览图

一级目录

二级目录

三级目录

读前须知

注意事项

  1. 同步场景优先:当前方案主要针对同步场景设计,以确保数据一致性和实时性。我们认识到并非所有场景都适用,因此将持续探索以适应更多数据交互模式。
  2. 乐观权限控制:我们采取了一种乐观的权限管理策略,即不是所有数据都需要进行严格的权限控制。我们专注于对那些确实需要保护的敏感数据实施权限管理。
  3. 半自动化实现:为确保业务逻辑的清晰性和减少操作歧义,我们采用了半自动化的实现方式。这种方法结合了配置的灵活性和编码的精确性,以实现特定数据的权限控制。
  4. 最小权限单位:我们以菜单(menu)而非数据库表(table)作为数据权限控制的最小单位。这样做避免了用户需要进行大量表配置操作的繁琐,提高了效率和实用性。
  5. 注解依赖性:核心功能依赖于@SaCheckPermission注解来实现。为了确保数据权限的正常运行,请确保所有需要实现权限控制的Controller层都正确配置了该注解。这将帮助系统自动识别并应用相应的权限规则。
  6. 数据归属规则:我们的数据归属规则基于数据创建时的用户状态。这意味着,当用户创建数据时,系统将根据用户当时的部门归属来标记数据。例如,如果张三同时属于财务部和市场部,并且他创建了一条数据,系统将记录这条数据的创建者为create_id="张三ID",并标注其部门范围为dept_scope="[财务部ID, 市场部ID]"。这样的设计确保了数据归属的明确性,并有助于实现跨部门的数据管理和权限控制。
  7. 最小范围规则:系统默认对(已开启但未配置具体权限的)菜单项限制用户访问,仅允许访问本人数据,以保障数据安全。

内置规则

我们的系统内置了五种灵活的数据权限规则,以满足不同场景下的需求:

  • 全部:用户被授予查看所有数据的权限,适用于需要全面数据访问的角色。
  • 本部门及以下:用户可以查看自己所在部门及其所有子部门的数据。为实现这一规则,我们采用了闭包表结构(sys_dept_closure表),确保了查询的效率和速度。
  • 仅本部门:用户只能访问自己所在部门的数据,适用于需要限制数据访问范围的情况。
  • 仅本人:用户仅能查看自己创建或负责的数据,这有助于保护数据的私密性和责任归属。
  • 自定义:提供高度灵活性,允许根据“用户维度”或“部门维度”来定制数据访问规则,满足特定或复杂的业务需求。

实现原理

数据结构

在我们的数据权限管理系统中,用户表、权限表、菜单表以及部门表之间建立了多对多的复杂关系。这种设计不仅增强了系统的灵活性,而且为不同的业务场景提供了广泛的支持。

  1. 用户与角色:用户可以拥有多个角色,而每个角色也可以被多个用户共享,从而实现了角色的灵活分配和权限的多样化管理。
  2. 角色与权限:每个角色可以关联多种权限,而同一种权限也可以被分配给不同的角色,确保了权限设置的细致和精确。
  3. 角色与菜单:角色与菜单之间的关系也是多对多的,允许角色访问多个菜单项,同时一个菜单项也可以被多个角色访问,提供了菜单访问的灵活性。
  4. 角色与部门:角色与部门之间的关系同样采用多对多模型,使得一个角色可以跨越不同部门,或者一个部门内可以有多个角色,增强了组织结构的适应性。

这种多对多的关系设计,不仅提高了系统的扩展性和可维护性,而且为实现复杂和动态的权限管理提供了坚实的基础。

在这里插入图片描述

UML时序

下述时序图详细展示了数据权限管理中的关键节点和操作流程。

在这里插入图片描述

核心逻辑

用户登录与权限规则预处理

[!NOTE]

dataScopeCd类型如下:

字典值含义是否自定义
1006001全部
1006002本部门及以下
1006003仅本部门
1006004仅本人
1006005自定义

代码参考:

在这里插入图片描述

为了显著提升系统性能并缩短响应时间,我们在用户登录时引入了一项关键的预处理机制。这一机制专注于对数据权限规则(Rule)进行有效管理和优化,分为两大类:非自定义权限配置和自定义权限配置。

非自定义权限配置处理
  • 系统将根据用户ID、用户角色所关联的菜单(menu)、所属部门等条件进行查询。
  • 查询结果将经过二次聚合,生成一个RuleMap,为非自定义权限提供结构化的规则集。
自定义权限配置处理
  • 类似地,系统根据用户ID、角色所关联的菜单(menu)、数据权限类型(dataScopeCd)等条件进行查询。
  • 自定义权限是独立的存在,他与非自定义权限是OR的关系。
合并规则细则
  • 最小权限最大规则:在相同菜单下,如果有多个部门的权限范围(scope),系统将选择最小值的scope作为最终权限(规则编号1006001至1006004)。
  • 单一权限无需合并:对于只有一个scope的菜单,不需要进行额外的合并步骤。
  • 自定义权限规则:对于具有自定义权限的用户,系统将分别获取其部门维度和用户维度的权限,做OR拼接。
RuleMap 权限规则映射

RuleMap 是一个关键的数据结构,用于存储和映射菜单(menu)与数据权限范围(dataScopeCd)之间的关系。这种映射以键值对(key-value)的形式存在,其中:

  • Key (k)menuId,代表菜单的唯一标识符。
  • Value (v)dataScopeCd,代表与该菜单关联的数据权限范围代码。

示例 JSON 格式的 RuleMap:

{"ruleMap": {"85b54322630f43a39296488a5e76ba16": "1006002"}
}

在这个示例中,"85b54322630f43a39296488a5e76ba16" 是一个特定的 menuId,而 "1006002" 是对应的数据权限范围代码。这表明对于具有该 menuId 的菜单项,用户的访问权限被限定为 dataScopeCd 所定义的范围。

RuleMap 的重要性:

  • RuleMap 提供了一个快速查询的机制,允许系统迅速确定用户对于特定菜单项的访问权限。
  • 它支持权限管理的灵活性和扩展性,便于在系统运行时动态调整权限设置。
  • 通过预处理和缓存 RuleMap,系统能够减少权限验证的计算成本,从而提高响应速度。

通过这种方式,RuleMap 成为了我们权限管理系统中的一个高效组件,确保了权限控制的精确性和性能的优化。

预处理Session数据结构与示例

在我们的系统中,预处理Session用于存储用户登录后所需的关键数据,以便快速访问和权限验证。以下是Session中包含的主要数据结构及其描述:

  1. 所属部门及其子孙节点部门 (deptAndChildren)
    • 这是一个列表,包含用户所属部门的ID以及其所有子孙部门的ID。
  2. 权限标识与菜单关系数组 (permissionAndMenuIds)
    • 这是一个映射,将权限标识符映射到菜单ID,明确了不同权限对应的菜单项。
  3. 菜单的查询规则 (ruleMap)
    • 这是一个映射,将菜单ID映射到数据权限范围代码,定义了每个菜单项的访问规则。
  4. 自定义userRule(userRuleMap)
    • 这是一个映射,将菜单ID映射到userId集合,定义了可以访问指定菜单的指定用户集合。
  5. 自定义deptRule (deptRuleMap)
    • 这是一个映射,将菜单ID映射到dept集合,定义了可以访问指定菜单的指定部门集合。

示例JSON格式的预处理Session数据:

{"depts": ["java.util.ArrayList",[16]],"deptAndChildren": ["java.util.ArrayList",[16]],"permissionAndMenuIds": {"@class": "java.util.HashMap","teacher.statistics.update": "85b54322630f43a39296488a5e76ba16","teacher.statistics.remove": "85b54322630f43a39296488a5e76ba16","teacher.statistics.export": "85b54322630f43a39296488a5e76ba16","teacher.statistics.query_table": "85b54322630f43a39296488a5e76ba16","teacher.statistics.create": "85b54322630f43a39296488a5e76ba16","teacher.statistics.import": "85b54322630f43a39296488a5e76ba16"},"ruleMap": {"@class": "java.util.HashMap","85b54322630f43a39296488a5e76ba16": "1006002"},"userRuleMap": {"@class": "java.util.LinkedHashMap","85b54322630f43a39296488a5e76ba16": ["java.util.HashSet",[3,5]]},"deptRuleMap": {"@class": "java.util.HashMap"}
}

在这个示例中,我们展示了如何将部门信息、权限与菜单的映射关系、查询规则以及自定义的用户和部门ID存储在Session中。这种结构化的数据存储方式,使得在用户进行操作时,系统能够快速地进行权限验证和数据访问控制。

数据权限控制实现

[!NOTE]

代码参考:

在这里插入图片描述

我们的数据权限控制流程基于Controller层的权限标识和RuleMap的结合来实现精确的数据访问控制:

  • 权限标识与Menu关联:通过分析Controller上的权限permission标识,我们能够确定Controller与相应菜单项(menu)的关联关系,这是权限控制的第一步。
  • RuleMap应用:结合RuleMap,我们可以进一步明确用户对特定菜单项的访问权限,为数据权限控制提供规则支持。

在实现数据权限控制时,我们采用了特定的SQL拼接技术来确保查询的权限正确性:

  • 部门权限查询:通过嵌套的EXISTS查询,我们检查用户所属部门是否包含在指定的部门ID列表中,确保只有当用户与部门关联时,数据才可见。示例SQL如下所示:

    String sql = "EXISTS (SELECT 1 FROM `sys_user_dept` WHERE `sys_user_dept`.`dept_id` IN (部门ID列表) AND `" + alias + "`.`" + field + "` = `sys_user_dept`.`user_id`)";
    
  • 超级管理员权限查询:对于具有超级管理员权限的用户,我们通过特定的字段检查来允许访问,示例SQL如下:

    String sqlSuper = "EXISTS (SELECT 1 FROM `sys_user` WHERE `sys_user`.`id` = `" + alias + "`.`" + field + "` AND `sys_user`.`user_tag_cd` = '1001002' AND `del_flag` = 'F')";
    
  • 个人权限查询:在处理个人权限时,我们使用了条件查询构造器来生成=语句,允许用户访问他们创建的数据。示例代码如下:

    // sql = ' and create_id = 1 ' 
    QueryCondition queryCondition = QueryCondition.create(new QueryColumn(table.getSchema(), table.getName(), field, table.getAlias()),SqlConsts.EQUALS,loginUser.getUserInfo().getId()
    );
    
  • 自定义权限查询:我们将部门权限和个人权限结合起来,以实现更为复杂的自定义数据访问权限控制。


使用指南

下面我来详细描述下,如何配置和使用数据权限,在使用数据权限前,先进行以下的准备工作。

准备工作

在开始配置数据权限之前,请完成以下准备工作:

  • 确保application.yml配置文件中启用了数据权限功能,设置配置参数:

    data-scope:enable: true# 数据逻辑实现最小验证(查询)单位:dept 部门(dept_scope字段)、user 用户 (create_id 字段),默认user。logic-min-unit: user
    
  • 确认需要进行数据权限控制的表具备create_id(创建者ID,整型)和dept_scope(部门范围,JSON格式)属性。(后续会修改属性结合自定义注解使用)

步骤一:新建菜单与设置权限
  1. 新建菜单项:在系统后台管理界面中,创建新的菜单项以代表不同的业务功能。

  2. 分配权限标识:为每个新建的菜单项分配一个唯一的permission标识符,该标识符将用于后续的权限控制逻辑。

  3. 开启数据权限支持:在菜单配置中找到并启用数据权限支持开关,确保该菜单项能够参与数据权限的控制流程。
    在这里插入图片描述

  4. 使用注解标识权限:在与菜单项关联的Controller层方法上,应用@SaCheckPermission注解,并填入之前分配的permission标识符,以明确该方法的权限要求。

  5. 确保注解正确性:检查注解的语法和permission标识符的准确性,确保它们与菜单项的权限设置相匹配。

    在这里插入图片描述


步骤二:SQL层添加权限标识

[!WARNING] !注意

SimpleDataScopeHelper 通过ThreadLocal存储每个线程的权限控制状态,以确保权限控制逻辑的隔离性和线程安全。为了防止ThreadLocal状态未被正确清理导致的潜在问题,我们强烈建议使用try-finally代码块来严格管理权限控制逻辑:

  • 初始化权限控制上下文: 在try块开始时,调用SimpleDataScopeHelper.start(YourEntity.class)初始化当前线程的权限控制上下文。这为接下来的数据访问操作设定了权限控制的边界。

    try {SimpleDataScopeHelper.start(TeacherStatistics.class);
    
  • 执行数据访问操作: 在try块中执行所有需要权限控制的数据访问业务逻辑。SimpleDataScopeHelper将基于当前线程的上下文自动应用相应的权限控制。

  • 清理权限控制状态finally块确保无论业务逻辑执行结果如何,都会调用SimpleDataScopeHelper.clearDataScope()来清理ThreadLocal状态。这一步是关键,它避免了ThreadLocal中的残留状态影响到其他业务操作。

    } finally {SimpleDataScopeHelper.clearDataScope();
    }
    

遵循这一最佳实践不仅有助于保持业务逻辑的清晰性和准确性,而且确保了应用程序的线程安全性和稳定性。

在实现数据权限控制的核心环节中,SQL层的适当配置至关重要。请按照以下步骤在SQL查询中添加权限控制标识:

  1. 确定权限控制点:识别出需要根据用户权限过滤数据的SQL查询点。
  2. 使用权限控制辅助类:在相关的数据访问代码块中,使用SimpleDataScopeHelper类来管理权限控制的逻辑。
  3. 示例代码
try {SimpleDataScopeHelper.start(TeacherStatistics.class); // 指定要追加权限条件的数据表PO实体类,对此po类的关联表进行条件追加。// 在此处编写或修改你的SQL查询,SimpleDataScopeHelper将自动根据权限规则追加条件// 示例:SELECT * FROM teacher_statistics WHERE 1=1 你的查询条件
} finally {SimpleDataScopeHelper.clearDataScope(); // 清除权限控制状态,避免影响其他操作
}
  1. 自定义SQL条件:如果需要,你可以在SimpleDataScopeHelper.start()clearDataScope()之间添加自定义的SQL条件,以满足特定的业务需求。
  2. 清理权限状态:在finally块中使用SimpleDataScopeHelper.clearDataScope()确保在操作完成后清理权限控制状态,以防止对系统中的其他操作产生影响。

通过这些步骤,您可以确保数据权限的配置既符合业务逻辑,又满足安全合规要求,同时为用户提供了清晰的权限边界。
例1:
在这里插入图片描述

例1:
在这里插入图片描述


步骤三:设置数据权限角色
  • 定义不同的数据权限角色,并为每个角色配置相应的权限规则。自定义权限外,不需单独配置部门,其依赖用户本身部门属性。

  • 自定义权限可以任意配置部门和用户的规则。

    非自定义Scope:

在这里插入图片描述

自定义Scope:

在这里插入图片描述


步骤四:关联用户与数据权限角色
  • 将用户与相应的数据权限角色进行关联,确保用户能够根据其角色获得正确的数据访问权限。

在这里插入图片描述


步骤五:测试验证
  • 在配置完成后,进行彻底的测试以验证数据权限的配置是否正确,确保权限控制按预期工作。

效果展示

为了直观展示数据权限控制的效果,我们以“教师统计”功能中的列表查询和Excel导出为例。以下是我们的演示设置:

账户与权限配置

我们准备了四个不同权限配置的账户,以展示不同数据权限对查询结果的影响:

账户部门数据权限设置
test1研发团队教师统计-本部门及以下部门
test2移动组教师统计-仅本部门
test3移动组教师统计-仅本人
test4无部门教师统计-自定义权限

权限效果验证

  • test1账户:具有查看“教师统计”数据的权限,包括其所属的“研发团队”及其所有子部门的数据。
  • test2账户:权限限制为仅查看其直接所属的“移动组”的数据。
  • test3账户:仅能查看其个人创建的“教师统计”数据。
  • test4账户:具有自定义权限,具体权限设置将根据实际业务需求进行配置。

操作步骤

  1. 登录账户:使用上述账户登录系统,密码默认为sz123456
  2. 访问教师统计列表:在系统中访问“教师统计”列表页面。
  3. 检查数据:观察并验证每个账户所看到的列表数据是否符合其数据权限设置的预期。

通过这一演示,您可以清晰地看到不同数据权限设置如何影响用户在系统中的数据访问结果。

预览图

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

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

相关文章

Nginx Proxy缓存

Proxy缓存 缓存类型 网页缓存 (公网)CDN数据库缓存 memcache redis网页缓存 nginx-proxy客户端缓存 浏览器缓存 模块 ngx_http_proxy_module 语法 缓存开关 Syntax: proxy_cache zone | off; Default: proxy_cache off; Context: http,…

Android Studio Build窗口出现中文乱码问题

刚安装成功的android studio软件打开工程,编译时下方build窗口中中文是乱码。 解决: 可点击studio状态栏的Help—>Edit Custom VM Options ,在打开的studio64.exe.vmoptions文件后面添加:(要注意不能有空格,否则st…

科技前沿:Llama 3.1的突破与革新

在科技的长河中,每一次模型的更新都是对人类智慧的致敬。今天,我们将聚焦于Meta公司最新发布的Llama 3.1系列模型,探索其在AI领域的前沿突破。 新模型的诞生 自去年以来,Meta公司不断推进人工智能技术的发展,终于在近…

Visual Studio 2022新建 cmake 工程测试 opencv helloworld

1. 参考博客: 1.1. https://blog.csdn.net/yangSHU21/article/details/130237669( 利用OpenCV把一幅彩色图像转换成灰度图 )( vs2022_cmake_test.cpp 中的代码用的此博客的,就改了下图片文件路径而已 ) 2. 检查 Visual Studio 2022是否支持 cmake&#…

大数据之Oracle同步Doris数据不一致问题

数据同步架构如下: 出现的问题: doris中的数据条数 源库中的数据条数 总数完全不一致。 出现问题的原因: 在Dinky中建立表结构时,缺少对主键属性的限制 primary key(ID) not enforced 加上如上语句,数据条数解决一致 …

Internxt:适用于Linux开源安全云存储平台

有无数的云存储平台为您的文件提供安全可靠的存储空间。可在 Linux 上安装的热门云存储应用程序包括Dropbox、Nextcloud和Google Drive,遗憾的是,后者迄今为止不提供 Linux 客户端。 其他自托管选项包括OwnCloud、Pydio Cells、Seafile、Resilio和Synct…

matlab 小数取余 rem 和 mod有 bug

目录 前言Matlab取余函数1 mod 函数1.1 命令行输入1.2 命令行输出 2 rem 函数2.1 命令行输入2.2 命令行输出 分析原因注意 前言 在 Matlab 代码中mod(0.11, 0.1) < 0.01 判断为真&#xff0c;mod(1.11, 0.1) < 0.01判断为假&#xff0c;导致出现意料外的结果。 结果发现…

Leetcode509. 斐波那契数(递归和常规两种写法)

问题描述&#xff1a; 斐波那契数 &#xff08;通常用 F(n) 表示&#xff09;形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n - 2)&#xff0c;…

走进数组的奇妙之旅(1)-学习笔记

引言&#xff1a; 在前几篇文章中&#xff0c;我们深入探讨了函数的奥秘。在讲述函数知识的过程中&#xff0c;我们邂逅了一个新的概念&#xff0c;你或许还记得在演示 strcpy函数时&#xff0c;出现的这行代码&#xff1a;char1[20]{0};。当时&#xff0c;你是否感到好奇&…

软考中级网络工程师考什么?应该怎么正确备考

网络工程师软考中级难易度50%&#xff0c;不太难。但是如果准备不足就悬了&#xff0c;赶紧备考起来吧。 网络工程师每年考两次&#xff0c;相比其他的软考考试一年中考的机会又多了一次&#xff0c;而且软考网工也是挺热门的科目&#xff0c;每年很多人报考&#xff0c;相对的…

视觉语言动作模型:从网页知识到机器人控制的实战RT-2

作者&#xff1a; Anthony Brohan, Noah Brown, Justice Carbajal, Yevgen Chebotar, Xi Chen, Krzysztof Choromanski, Tianli Ding, Danny Driess, Avinava Dubey, Chelsea Finn, Pete Florence, Chuyuan Fu, Montse Gonzalez Arenas, Keerthana Gopalakrishnan, Kehang Han…

【深度学习】PyTorch框架(5):Transformer和多注意力机制

1、引言 在本文中&#xff0c;我们将探讨近两年来最具影响力的模型架构之一——Transformer模型。自从2017年Vaswani等人发表的论文《注意力是你所需要的全部》以来&#xff0c;Transformer架构在多个领域持续刷新着性能记录&#xff0c;尤其是在自然语言处理&#xff08;NLP&…

CSI-RS在信道中传输的过程

简单介绍CSI-RS信号生成&#xff0c;在信道中传输和接收的过程 1.载波配置 首先需要配置载波相关的参数 系统带宽和子载波间隔 5G NR中&#xff0c;系统带宽和子载波间隔是两个关键参数&#xff0c;共同决定无线资源的分配和使用 系统带宽 5G NR支持广泛的系统带宽&…

碳酸锂溶液树脂吸附除钙镁的方法和工艺流程

碳酸锂溶液作为一种重要的化工原料&#xff0c;主要用于锂电池的制造和其他化学合成过程。它对纯度有较高要求&#xff0c;因此在制备和处理过程中&#xff0c;去除杂质如钙镁离子是非常关键的步骤。 关于碳酸锂溶液除钙镁的方法&#xff0c;几种常见的处理技术包括沉淀法、离…

C++ | Leetcode C++题解之第264题丑数II

题目&#xff1a; 题解&#xff1a; class Solution { public:int nthUglyNumber(int n) {vector<int> dp(n 1);dp[1] 1;int p2 1, p3 1, p5 1;for (int i 2; i < n; i) {int num2 dp[p2] * 2, num3 dp[p3] * 3, num5 dp[p5] * 5;dp[i] min(min(num2, num3…

解决R语言找不到系统库导致的报错

1、基本需知 1.1、系统库 系统库&#xff08;System library&#xff09;是一组预先编写和编译好的软件模块集合&#xff0c;用于支持操作系统的基本功能和提供一些常见的服务。这些库通常由操作系统或第三方开发者提供&#xff0c;并且在系统安装过程中被预装或者用户可以额…

springboot+vue+mybatis高校宿舍管理系统+PPT+论文+讲解+售后

随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于高校宿舍管理系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了高校宿舍管理系统&#xff0c;它彻底改变了过去传统…

【Linux】从零开始认识多线程 --- 线程互斥

人生有许多事情 正如船后的波纹 总要过后才觉得美的 -- 余光中 线程互斥 1 线程类的封装1.1 框架搭建1.2 线程启动1.3 线程终止1.4 线程等待1.5 运行测试 2 线程互斥2.1 多线程访问的问题2.2 解决办法 --- 锁2.3 从原理角度理解锁 Thanks♪(&#xff65;ω&#xff65;)&am…

Java语言程序设计——篇七(1)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 继承 类的继承实战演练 方法覆盖实战演练 &#x1f351;super关键字实战演练 调用父类的构造方法 类的继承 通过类的继承方式&#xff0c;可以…

手机图片如何转化为word文档?分享3种好用的软件。

在数字化时代&#xff0c;手机已经成为我们生活中不可或缺的一部分。随着手机拍照功能的日益强大&#xff0c;我们常常用手机记录下重要的信息和瞬间。但你有没有遇到过这样的烦恼&#xff1a;如何将手机里的图片快速转化为可编辑的Word文档呢&#xff1f;今天&#xff0c;就为…