mybatis-plus多租戶兼容多字段租戶標識

默認租戶插件處理器的缺陷

在springboot工程中引入mybatis-plus的租戶插件TenantLineInnerInterceptor,能簡化我們的數據隔離操作,例如各類含租戶用戶登錄權限的rest接口中,不需要再根據登錄用戶-set租戶條件-觸發查詢,租戶插件能幫我們省略手動插入條件的繁瑣過程。

mybatis-plus默認僅支持單個字段的租戶條件,實際使用場景中,我們的“租戶”在系統中,可能是一個多類型的數據概念,例如菜單大類模塊一可能一種用戶能訪問,菜單大類模塊二是另一種用戶能訪問,兩種模塊用戶權限都在同一管理菜單、權限、用戶中進行配置,即用戶區分類型,不同類型租戶id屬性來源不同。由于不同大類模塊字段可能不一致,即“TenantId”在不同表中,是不同的字段名稱,這時候使用原始的租戶插件接口,就滿足不了需求了。

public interface TenantLineHandler {/*** 獲取租戶 ID 值表達式,只支持單個 ID 值** @return 租戶 ID 值表達式*/Expression getTenantId();/*** 獲取租戶字段名* 默認字段名叫: tenant_id** @return 租戶字段名*/default String getTenantIdColumn() {return "tenant_id";}/*** 根據表名判斷是否忽略拼接多租戶條件* 默認都要進行解析并拼接多租戶條件** @param tableName 表名* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租戶條件*/default boolean ignoreTable(String tableName) {return false;}/*** 忽略插入租戶字段邏輯** @param columns        插入字段* @param tenantIdColumn 租戶 ID 字段* @return*/default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));}
}

上述接口中getTenantId和getTenantIdColumn是唯一的,無法區分不同表不同租戶字段。

針對多字段條件租戶插件的實現

改造步驟如下:

1、定義一個新的租戶數據行處理器

如這里命名FixTenantLineHandler,

僅更新getTenantId和getTenantIdColumn方法,方便根據表名來判斷取什么租戶字段。

package com.infypower.vpp.security.permission;import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.schema.Column;import java.util.List;/*** 租戶處理器( TenantId 行級 )** @author endcy* @see com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler*/
public interface FixTenantLineHandler {/*** 獲取租戶 ID 值表達式,只支持單個 ID 值* <p>** @return 租戶 ID 值表達式*/Expression getTenantId(String tableName);/*** 獲取租戶字段名* <p>* 默認字段名叫: tenant_id** @return 租戶字段名*/default String getTenantIdColumn(String tableName) {return "tenant_id";}/*** 根據表名判斷是否忽略拼接多租戶條件* <p>* 默認都要進行解析并拼接多租戶條件** @param tableName 表名* @return 是否忽略, true:表示忽略,false:需要解析并拼接多租戶條件*/default boolean ignoreTable(String tableName) {return false;}/*** 忽略插入租戶字段邏輯** @param columns        插入字段* @param tenantIdColumn 租戶 ID 字段* @return*/default boolean ignoreInsert(List<Column> columns, String tenantIdColumn) {return columns.stream().map(Column::getColumnName).anyMatch(i -> i.equalsIgnoreCase(tenantIdColumn));}
}

2、實現自定義TenantLineHandler

下列TenantSecurityUtils是用戶登錄時注入的requestScope或者ThreadLocal存儲的用戶信息,包含不同租戶類型信息及ID、管理員租戶數據授權配置信息等,可自定義實現。

hasProperty方法會取mybatis-plus緩存的表信息,根據表名判斷表映射屬性是否包含不同租戶id字段,這樣做的好處是無需額外編碼處理不同表對應不同屬性名稱配置判斷,直接使用mybatis-plus原有的TableInfoHelper相關功能。

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.gitee.coadmin.enums.UserIdentityTypeEnum;
import com.gitee.coadmin.utils.TenantSecurityUtils;
import com.infypower.vpp.common.CesConstant;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.schema.Column;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;/*** ...** @author endcy* @date 2025/9/9*/
@Slf4j
@Component
public class FixTenantLineInnerInterceptor implements FixTenantLineHandler {private static final String TENANT_ID1_COLUMN = "tenant_id1";private static final String TENANT_ID1_NAME = "tenantId1";private static final String TENANT_ID2_COLUMN = "tenant_id2";private static final String TENANT_ID2_NAME = "tenantId2";private static final String TENANT_ID3_COLUMN = "tenant_id3";private static final String TENANT_ID3_NAME = "tenantId3";//存在租戶id 字段但忽略租戶過濾的數據表 逐行加private final List<String> IGNORE_TABLES = CollUtil.newArrayList("");@Overridepublic Expression getTenantId(String tableName) {UserIdentityTypeEnum identityType = TenantSecurityUtils.getCurrentUserIdentityType();//如果是平臺權限用戶 可能有需要過濾租戶類型1數據if (identityType == UserIdentityTypeEnum.PLATFORM) {Set<Long> tenantIds = TenantSecurityUtils.getCurrentPlatformUserTenantId1List();if (CollUtil.isEmpty(tenantIds)) {//默認返回平臺管理權限return new LongValue(0L);}List<Expression> valueList = tenantIds.stream().map(LongValue::new).collect(Collectors.toList());return new InExpression(new Column(getTenantIdColumn(tableName)),new ExpressionList(valueList));}return new LongValue(TenantSecurityUtils.getCurrentUserMajorIdentityId());}@Overridepublic String getTenantIdColumn(String tableName) {String tenantColumn = TENANT_ID1_COLUMN;//平臺理員綁定了特定租戶權限if (hasProperty(tableName, TENANT_ID1_NAME) && hasProperty(tableName, TENANT_ID2_NAME)) {tenantColumn = TENANT_ID1_COLUMN;if (TenantSecurityUtils.getCurrentUserIdentityType() == UserIdentityTypeEnum.TENANT1) {tenantColumn = TENANT_ID2_COLUMN;}} else if (hasProperty(tableName, TENANT_ID2_NAME)) {tenantColumn = TENANT_ID2_COLUMN;} else if (hasProperty(tableName, TENANT_ID3_NAME)) {tenantColumn = TENANT_ID3_COLUMN;}//默認返回主租戶字段log.debug(">>>> table:{} tenantColumn:{}", tableName, tenantColumn);return tenantColumn;}@Overridepublic boolean ignoreTable(String tableName) {boolean ignore;if (IGNORE_TABLES.contains(tableName)) {return true;}//全數據管理員略過if (TenantSecurityUtils.getCurrentUserMajorIdentityId() == 0L&& CollUtil.isEmpty(TenantSecurityUtils.getCurrentPlatformUserTenantId1List())) {return true;}if (!hasProperty(tableName, TENANT_ID1_NAME)&& !hasProperty(tableName, TENANT_ID2_NAME)&& !hasProperty(tableName, TENANT_ID3_NAME)) {ignore = true;log.debug(">>>> ignore tenant data for table:{}", tableName);} else {ignore = false;}if (ignore) {IGNORE_TABLES.add(tableName);return true;}
//        ignore = checkIgnoreTable(tableName);return false;}private boolean checkIgnoreTable(String tableName) {if (IGNORE_TABLES.contains(tableName.toLowerCase())) {return true;}//其他校驗邏輯待拓展return false;}public static boolean hasProperty(String tableName, String propertyName) {TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);if (tableInfo == null) {return false;}// 檢查字段列表是否包含該屬性return tableInfo.getFieldList().stream().anyMatch(field -> field.getProperty().equals(propertyName));}}

3、重寫租戶sql注入處理器

參考原TenantLineInnerInterceptor實現,使用tenantLineHandler,并替換需要傳入表名的調用

package com.infypower.vpp.security.permission;import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.PropertyMapper;
import lombok.*;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
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.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;/*** @author endcy* @since 2029/9/9* @see com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
@SuppressWarnings({"rawtypes"})
public class FixTenantLineInnerInterceptor2 extends JsqlParserSupport implements InnerInterceptor {private FixTenantLineHandler tenantLineHandler;@Overridepublic void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {……}@Overridepublic void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {……}@Overrideprotected void processSelect(Select select, int index, String sql, Object obj) {……}protected void processSelectBody(SelectBody selectBody) {……}@Overrideprotected void processInsert(Insert insert, int index, String sql, Object obj) {String tableName = insert.getTable().getName();if (tenantLineHandler.ignoreTable(tableName)) {// 過濾退出執行return;}List<Column> columns = insert.getColumns();if (CollectionUtils.isEmpty(columns)) {// 針對不給列名的insert 不處理return;}String tenantIdColumn = tenantLineHandler.getTenantIdColumn(tableName);if (tenantLineHandler.ignoreInsert(columns, tenantIdColumn)) {// 針對已給出租戶列的insert 不處理return;}columns.add(new Column(tenantIdColumn));// fixed gitee pulls/141 duplicate updateList<Expression> duplicateUpdateColumns = insert.getDuplicateUpdateExpressionList();if (CollectionUtils.isNotEmpty(duplicateUpdateColumns)) {EqualsTo equalsTo = new EqualsTo();equalsTo.setLeftExpression(new StringValue(tenantIdColumn));equalsTo.setRightExpression(tenantLineHandler.getTenantId(tableName));duplicateUpdateColumns.add(equalsTo);}Select select = insert.getSelect();if (select != null) {this.processInsertSelect(tableName, select.getSelectBody());} else if (insert.getItemsList() != null) {// fixed github pull/295ItemsList itemsList = insert.getItemsList();if (itemsList instanceof MultiExpressionList) {((MultiExpressionList) itemsList).getExpressionLists().forEach(el -> el.getExpressions().add(tenantLineHandler.getTenantId(tableName)));} else {((ExpressionList) itemsList).getExpressions().add(tenantLineHandler.getTenantId(tableName));}} else {throw ExceptionUtils.mpe("Failed to process multiple-table update, please exclude the tableName or statementId");}}/*** update 語句處理*/@Overrideprotected void processUpdate(Update update, int index, String sql, Object obj) {final Table table = update.getTable();if (tenantLineHandler.ignoreTable(table.getName())) {// 過濾退出執行return;}update.setWhere(this.andExpression(table, update.getWhere()));}/*** delete 語句處理*/@Overrideprotected void processDelete(Delete delete, int index, String sql, Object obj) {……}/*** delete update 語句 where 處理*/protected BinaryExpression andExpression(Table table, Expression where) {//獲得where條件表達式EqualsTo equalsTo = new EqualsTo();equalsTo.setLeftExpression(this.getAliasColumn(table));equalsTo.setRightExpression(tenantLineHandler.getTenantId(table.getName()));if (null != where) {if (where instanceof OrExpression) {return new AndExpression(equalsTo, new Parenthesis(where));} else {return new AndExpression(equalsTo, where);}}return equalsTo;}/*** 處理 insert into select* <p>* 進入這里表示需要 insert 的表啟用了多租戶,則 select 的表都啟動了** @param selectBody SelectBody*/protected void processInsertSelect(String tableName, SelectBody selectBody) {PlainSelect plainSelect = (PlainSelect) selectBody;FromItem fromItem = plainSelect.getFromItem();if (fromItem instanceof Table) {// fixed gitee pulls/141 duplicate updateprocessPlainSelect(plainSelect);appendSelectItem(tableName, plainSelect.getSelectItems());} else if (fromItem instanceof SubSelect) {SubSelect subSelect = (SubSelect) fromItem;appendSelectItem(tableName, plainSelect.getSelectItems());processInsertSelect(tableName, subSelect.getSelectBody());}}/*** 追加 SelectItem** @param selectItems SelectItem*/protected void appendSelectItem(String tableName, List<SelectItem> selectItems) {if (CollectionUtils.isEmpty(selectItems))return;if (selectItems.size() == 1) {SelectItem item = selectItems.get(0);if (item instanceof AllColumns || item instanceof AllTableColumns)return;}selectItems.add(new SelectExpressionItem(new Column(tenantLineHandler.getTenantIdColumn(tableName))));}/*** 處理 PlainSelect*/protected void processPlainSelect(PlainSelect plainSelect) {……}/*** 處理where條件內的子查詢** @param where where 條件*/protected void processWhereSubSelect(Expression where) {……}protected void processSelectItem(SelectItem selectItem) {……}/*** 處理函數* <p>支持: 1. select fun(args..) 2. select fun1(fun2(args..),args..)<p>* <p> fixed gitee pulls/141</p>** @param function*/protected void processFunction(Function function) {ExpressionList parameters = function.getParameters();if (parameters != null) {parameters.getExpressions().forEach(expression -> {if (expression instanceof SubSelect) {processSelectBody(((SubSelect) expression).getSelectBody());} else if (expression instanceof Function) {processFunction((Function) expression);}});}}/*** 處理子查詢等*/protected void processFromItem(FromItem fromItem) {……}/*** 處理 joins** @param joins join 集合*/private void processJoins(List<Join> joins) {……}/*** 處理聯接語句*/protected void processJoin(Join join) {……}/*** 處理條件*/protected Expression builderExpression(Expression currentExpression, Table table) {EqualsTo equalsTo = new EqualsTo();equalsTo.setLeftExpression(this.getAliasColumn(table));equalsTo.setRightExpression(tenantLineHandler.getTenantId(table.getName()));if (currentExpression == null) {return equalsTo;}if (currentExpression instanceof OrExpression) {return new AndExpression(new Parenthesis(currentExpression), equalsTo);} else {return new AndExpression(currentExpression, equalsTo);}}/*** 租戶字段別名設置* <p>tenantId 或 tableAlias.tenantId</p>** @param table 表對象* @return 字段*/protected Column getAliasColumn(Table table) {StringBuilder column = new StringBuilder();if (table.getAlias() != null) {column.append(table.getAlias().getName()).append(StringPool.DOT);}column.append(tenantLineHandler.getTenantIdColumn(table.getName()));return new Column(column.toString());}@Overridepublic void setProperties(Properties properties) {PropertyMapper.newInstance(properties).whenNotBlank("tenantLineHandler",ClassUtils::newInstance, this::setTenantLineHandler);}
}

4、注入自定義租戶插件

在mybatis-plus配置類中注入對應租戶插件及其他數據權限插件等。

package com.infypower.vpp.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DataPermissionInterceptor;
import com.infypower.vpp.security.permission.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** 配置文件* @author endcy* @since 2025/9/9*/
@Slf4j
@Configuration
@RequiredArgsConstructor
public class MybatisPlusConfig {private final FixTenantLineInnerInterceptor tenantLineInnerInterceptor;/*** admin模塊MP插件* 多租戶插件、數據權限插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 租戶代理interceptor.addInnerInterceptor(new FixTenantLineInnerInterceptor(tenantLineInnerInterceptor));// 其他數據權限,根據用戶配置進行數據權限控制DataPermissionInterceptor xxxPermissionInterceptor = new ResourceUserPermissionInterceptor();resourceUserPermissionInterceptor.setDataPermissionHandler(new XxxPermissionHandler());interceptor.addInnerInterceptor(xxxPermissionInterceptor);return interceptor;}}

其他配置保持不變。

上述getTenantId和getTenantIdColumn即核心實現,根據不同的用戶類型,賦不同的租戶字段標識和租戶id信息。相比于網上其他多字段租戶方案,本方案不依賴額外配置,不需要復雜解析,應該是最簡潔且拓展最為便捷的方式。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/96781.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/96781.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/96781.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HBase高級特性(布隆過濾器和協處理器)、列族設計、rowkey設計以及熱點問題處理

在闡述HBase高級特性和熱點問題處理前&#xff0c;首先回顧一下HBase的特點&#xff1a;分布式、列存儲、支持實時讀寫、存儲的數據類型都是字節數組byte[]&#xff0c;主要用來處理結構化和半結構化數據&#xff0c;底層數據存儲基于hdfs。 同時&#xff0c;HBase和傳統數據庫…

redis sentinel 與 clauster 的區別

Redis Sentinel(哨兵)和Redis Cluster(集群)是Redis提供的兩種不同的高可用和擴展性解決方案,它們的設計目標和適用場景有顯著區別: 1. 核心功能與目標 Redis Sentinel 主要解決主從架構的高可用問題,實現自動故障轉移 監控主從節點狀態,當主節點故障時自動將從節點提…

MySQL數據庫中快速導入大數據sql

1.PwerShell命令頁面導入全表數據庫 -P3310 指定數據庫端口號Get-Content "本地sql文件目錄" | .\mysql -u root -p -P 33102.PwerShell命令頁面導入單表到數據庫 -P3310 指定數據庫端口號Get-Content "本地sql文件目錄" | .\mysql -u root -p -P 3310 數…

消息類型proto的編寫和生成

消息類型proto的編寫和生成 代碼如下&#xff1a; syntax"proto3"; package xypmq;enum ExchangeType {UNKNOWNTYPE0;DIRECT1;FANOUT2;TOPIC3; };enum DeliveryMode {UNKNOWNMODE0;UNDURABLE1;DURABLE2; };message BasicProperties {string id1;DeliveryMode deliver…

Vuetify:構建優雅Vue應用的Material Design組件庫

Vuetify是一個基于Material Design設計規范的Vue.js UI組件庫&#xff0c;它提供了80多個精心設計的組件&#xff0c;幫助開發者快速構建美觀且功能豐富的企業級應用。核心特性1. 完整的Material Design實現// 所有組件遵循Material Design規范 <v-btn color"primary&q…

SpringBoot 注解深剖:@RequestParam 與 @RequestBody 的終極對決,90% 的開發者都踩過這些坑!

在 SpringBoot 開發中&#xff0c;處理 HTTP 請求參數是我們每天都要面對的工作。而RequestParam和RequestBody這兩個注解&#xff0c;就像是我們手中的兩把利劍&#xff0c;既能高效解決問題&#xff0c;用不好也可能 "誤傷" 自己。作為一名資深 Java 開發者&#x…

【Docker】P2 Docker環境構建準備:MacOS 與 Linux

目錄操作系統與 Docker 的兼容性分析Docker 技術本質MacOS 環境下的 Docker 構建1. 安裝前準備2. Docker Desktop安裝3. 鏡像加速配置高級操作&#xff1a;文件共享配置Linux 環境下的 Docker 構建卸載歷史版本配置軟件源Docker 核心組件安裝系統服務配置鏡像加速器配置應用配置…

OpenCV 發票識別全流程:透視變換與輪廓檢測詳解

目錄 前言 一、核心技術原理&#xff1a;透視變換與輪廓檢測 1. 透視變換&#xff1a;讓傾斜發票 “正過來” &#xff08;1&#xff09;什么是透視變換&#xff1f; &#xff08;2&#xff09;透視變換的 5 個關鍵步驟 2. 輪廓檢測&#xff1a;精準定位發票區域 &#x…

并發:使用volatile和不可變性實現線程安全

《Java并發編程實戰》中的VolatileCachedFactorizer展示了如何使用volatile和不可變性來實現線程安全。解決了簡單緩存實現中可能出現的線程安全問題&#xff0c;同時避免了全量同步帶來的性能開銷。 場景背景 假設有一個服務&#xff08;如因數分解服務&#xff09;&#xff0…

Linux x86 stability和coredump

1 POSIX pthread_create原理 1&#xff09;fork()、pthread_create()、vfork()對應的系統調用分別是sys_fork()、sys_clone()、sys_vfork()&#xff0c;它們在內核中都是通過do_fork()實現的。 2&#xff09;系統中所有的進程都組織在init_task.tasks鏈表下面&#xff0c;每個進…

【PyTorch】多對象分割

對象分割任務的目標是找到圖像中目標對象的邊界。實際應用例如自動駕駛汽車和醫學成像分析。這里將使用PyTorch開發一個深度學習模型來完成多對象分割任務。多對象分割的主要目標是自動勾勒出圖像中多個目標對象的邊界。 對象的邊界通常由與圖像大小相同的分割掩碼定義&#xf…

RabbitMQ---面試題

總結我們所學內容&#xff0c;這里推薦博客進行復習 RabbitMQ---面試題_rabbitmq常問面試題-CSDN博客

MasterGo自動布局(Auto Layout)

自動布局是用來表示 子元素與子元素之間互相影響的一種排版方式,是一種響應式布局技術。一般是將所有元素設計完成后再使用自動布局進行設置。 自動布局就是響應式布局,就是在不同尺寸的手機上寬度不同都應該怎么展示。 一般頁面的一級元素使用約束進行相對定位,二級元素及里…

還在重啟應用改 Topic?Spring Boot 動態 Kafka 消費的“終極形態”

場景描述&#xff1a; 你的一個微服務正在穩定地消費 Kafka 的 order_topic。現在&#xff0c;上游系統為了做業務隔離&#xff0c;新增加了一個 order_topic_vip&#xff0c;并開始向其中投遞 VIP 用戶的訂單。你需要在不重啟、不發布新版本的情況下&#xff0c;讓你現有的消費…

使用vllm部署neo4j的text2cypher-gemma-2-9b-it-finetuned-2024v1模型

使用vllm部署neo4j的text2cypher-gemma-2-9b-it-finetuned-2024v1模型 系統環境準備 由于使用的基于 nvcr.io/nvidia/cuda:12.1.1-cudnn8-runtime-ubuntu22.04 的 workbench,需要進行以下準備(其他系統環境可忽略) ldconfig -p | grep libcudnn 找到 libcudnn 的so庫,然…

Coze源碼分析-資源庫-創建知識庫-前端源碼-核心組件

概述 本文深入分析Coze Studio中用戶創建知識庫功能的前端實現。該功能允許用戶在資源庫中創建、編輯和管理知識庫資源&#xff0c;為開發者提供了強大的知識管理和數據處理能力。通過對源碼的詳細解析&#xff0c;我們將了解從資源庫入口到知識庫配置彈窗的完整架構設計、組件…

基于時空數據的網約車訂單需求預測與調度優化

一、引言隨著共享出行行業的蓬勃發展&#xff0c;網約車已成為城市交通的重要組成部分。如何精準預測訂單需求并優化車輛調度&#xff0c;是提升平臺運營效率、改善用戶體驗的關鍵。本文提出一種基于時空數據的網約車訂單需求預測與調度優化方案&#xff0c;通過網格化城市空間…

數據結構 Java對象的比較

在Java中&#xff0c;凡是涉及到比較的&#xff0c;可以分為兩類情況&#xff1a;一類是基本數據類型的比較&#xff0c;另一類是引用數據類型的比較。對于基本數據類型的比較&#xff0c;我們通過關系運算符&#xff08;、>、<、!、>、<&#xff09;進行它們之間的…

企智匯建筑施工項目管理系統:全周期數字化管控,賦能工程企業降本增效!?建筑工程項目管理軟件!建筑工程項目管理系統!建筑項目管理軟件企智匯軟件

在建筑施工行業&#xff0c;項目進度滯后、成本超支、質量安全隱患頻發、多方協同不暢等問題&#xff0c;一直是制約企業發展的痛點。傳統依賴人工記錄、Excel 統計的管理模式&#xff0c;不僅效率低下&#xff0c;更易因信息斷層導致決策失誤。企智匯建筑施工項目管理系統憑借…

k8s-臨時容器學習

臨時容器學習1. 什么是臨時容器2. 實驗1. 什么是臨時容器 在官網&#xff1a;https://kubernetes.io/zh-cn/docs/concepts/workloads/pods/ephemeral-containers/ 中有介紹 臨時容器是用于調試Pod中崩潰的容器或者不具備調試工具&#xff0c;比如在一個運行著業務的容器中&am…