Dify智能体平台源码二次开发笔记(5) - 多租户的SAAS版实现(2)

news2025/5/22 11:20:57

目录

前言

用户的查询

controller层

添加路由

service层

用户的添加

controller层

添加路由

service层-添加用户

service层-添加用户和租户关系

验证结果

结果


前言


完成租户添加功能后,下一步需要实现租户下的用户管理。基础功能包括:查询租户用户列表接口,添加用户接口

用户的查询

controller层
class AccountListApi(Resource):
    """Resource for getting account list."""

    @validate_token
    def get(self):
        """Get account list."""
        parser = reqparse.RequestParser()
        parser.add_argument("tenant_id", type=str, required=True, location="json")
        parser.add_argument("page", type=int, required=False, default=1, location="args")
        parser.add_argument("limit", type=int, required=False, default=20, location="args")
        parser.add_argument("search", type=str, required=False, location="args")
        args = parser.parse_args()

        accounts = AccountService.get_accounts_by_tenant(
            tenant_id=args["tenant_id"],
            page=args["page"],
            limit=args["limit"],
            search=args["search"]
        )

        return {
            "result": "success",
            "data": accounts
        }
添加路由
api.add_resource(AccountListApi, "/accounts")
service层
@staticmethod
    def get_accounts_by_tenant(tenant_id: str, page: int = 1, limit: int = 20, search: str = None, status: str = None) -> dict:
        query = (
            db.session.query(Account, TenantAccountJoin.role)
            .select_from(Account)
            .join(TenantAccountJoin, Account.id == TenantAccountJoin.account_id)
            .filter(TenantAccountJoin.tenant_id == tenant_id)
        )
        if search:
            search = f"%{search}%"
            query = query.filter(
                db.or_(
                    Account.name.ilike(search),
                    Account.email.ilike(search)
                )
            )
        if status:
            query = query.filter(Account.status == status)
        query = query.order_by(Account.name, Account.email)
        total = query.count()
        query = query.offset((page - 1) * limit).limit(limit)
        results = query.all()
        account_list = [{
            "id": str(account[0].id),
            "name": account[0].name,
            "email": account[0].email,
            "created_at": account[0].created_at.isoformat(),
            "role": account[1]
        } for account in results]
        return {
            'items': account_list,
            'total': total,
            'page': page,
            'limit': limit
        }

用户的添加

controller层
class AccountCreateApi(Resource):
    """Resource for creating a new account."""

    @validate_token
    def post(self):
        """Create a new account."""
        parser = reqparse.RequestParser()
        parser.add_argument("name", type=str, required=True, location="json")
        parser.add_argument("email", type=str, required=True, location="json")
        parser.add_argument("password", type=str, required=True, location="json")
        parser.add_argument("tenant_id", type=str, required=True, location="json")
        args = parser.parse_args()

        account = current_user
        tenant = TenantService.get_tenant_by_id(args["tenant_id"])
        if not tenant:
            return {"result": "fail", "message": "Tenant not found"}, 404

        try:
            new_account = AccountService.create_account(
                email=args["email"],
                name=args["name"],
                interface_language="zh-Hans",
                password=args["password"]
            )
            TenantService.create_tenant_member(tenant, new_account, role="owner")
            return {
                "result": "success",
                "data": {
                    "id": str(new_account.id),
                    "name": new_account.name,
                    "email": new_account.email,
                    "created_at": new_account.created_at.isoformat()
                }
            }
        except Exception as e:
            return {"result": "error", "message": str(e)}, 400

传入租户id和用户信息,我这里就直接默认语言是中文。

添加路由
api.add_resource(AccountCreateApi, "/accounts/create")
service层-添加用户
@staticmethod
    def create_account(
        email: str,
        name: str,
        interface_language: str,
        password: Optional[str] = None,
        interface_theme: str = "light",
        is_setup: Optional[bool] = False,
    ) -> Account:
        """create account"""
        # if not FeatureService.get_system_features().is_allow_register and not is_setup:
        #     from controllers.console.error import AccountNotFound
        #
        #     raise AccountNotFound()

        if dify_config.BILLING_ENABLED and BillingService.is_email_in_freeze(email):
            raise AccountRegisterError(
                description=(
                    "This email account has been deleted within the past "
                    "30 days and is temporarily unavailable for new account registration"
                )
            )

        account = Account()
        account.email = email
        account.name = name

        if password:
            # generate password salt
            salt = secrets.token_bytes(16)
            base64_salt = base64.b64encode(salt).decode()

            # encrypt password with salt
            password_hashed = hash_password(password, salt)
            base64_password_hashed = base64.b64encode(password_hashed).decode()

            account.password = base64_password_hashed
            account.password_salt = base64_salt

        account.interface_language = interface_language
        account.interface_theme = interface_theme

        # Set timezone based on language
        account.timezone = language_timezone_mapping.get(interface_language, "UTC")

        db.session.add(account)
        db.session.commit()
        return account
service层-添加用户和租户关系
@staticmethod
    def create_tenant_member(tenant: Tenant, account: Account, role: str = "normal") -> TenantAccountJoin:
        """Create tenant member"""
        if role == TenantAccountRole.OWNER.value:
            if TenantService.has_roles(tenant, [TenantAccountRole.OWNER]):
                logging.error(f"Tenant {tenant.id} has already an owner.")
                raise Exception("Tenant already has an owner.")

        ta = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=account.id).first()
        if ta:
            ta.role = role
        else:
            ta = TenantAccountJoin(tenant_id=tenant.id, account_id=account.id, role=role)
            db.session.add(ta)

        db.session.commit()
        return ta

这里直接调用已有方法

TenantAccountJoin
验证结果

用户表

用户租户关联表

结果

用创建的用户和密码从前端登录进去后,智能体、知识库、插件、模型等都完全隔离了。

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

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

相关文章

Linux的目录结构(介绍,具体目录结构)

目录 介绍 具体目录结构 简洁的目录解释 详细的目录解释 介绍 Linux的文件系统是采用级层式的树状目录结构,在此结构的最上层是根目录“/”。Linux的世界中,一切皆文件(比如:Linux会把硬件映射成文件来管理) 具体目…

如何用 esProc 补充数据库 SQL 的缺失能力

某些数据库 SQL 缺失必要的能力,通常要编写大段的代码,才能间接实现类似的功能,有些情况甚至要改用存储过程,连结构都变了。常见的比如:生成时间序列、保持分组子集、动态行列转换、自然序号、相对位置、按序列和集合生…

晶晨线刷工具下载及易错点说明:Key文件配置错误/mac剩余数为0解决方法

晶晨线刷工具下载及易错点说明:Key文件配置错误/mac剩余数为0解决方法 各种版本晶晨线刷工具下载: 晶晨线刷工具易出错点故障解决方法: 1、晶晨线刷工具加载固件的时候提示mac红字且剩余数为0的解决办法 很多同学可能会与遇到加…

论文阅读:Invertible Grayscale

这是一篇 ACM Transactions on Graphic 上的文章,这篇文章中介绍的应用还挺有意思的,关于可逆的图像灰度化。 Abstract 一旦彩色图像被转换为灰度图像,人们普遍认为,即使采用最先进的彩色化方法,原始颜色也无法完全恢…

关于ResNet和FPN的一份介绍

在这篇文章中我将介绍ResNet和FPN这两个深度学习中重要的技术。 一、ResNet-50/101 首先我们先来看ResNet技术: 1.1 概述 ResNet技术是基于残差学习,引入Bottleneck技术以及Shortcut Connection技术,而去解决神经网络中的退化问题。 1.2…

AI大模型学习九:‌Sealos cloud+k8s云操作系统私有化一键安装脚本部署完美教程

一、说明 ‌Sealos‌是一款基于Kubernetes(K8s)的云操作系统发行版,它将K8s以及常见的分布式应用如Docker、Dashboard、Ingress等进行了集成和封装,使得用户可以在不深入了解复杂的K8s底层原理的情况下,快速搭建起一个…

详解关于VS配置好Qt环境之后但无法打开ui界面

目录 找到Qt安装目录中designer.exe的路径 找到vs中的解决方案资源管理器 右键ui文件,找到打开方式 点击添加 然后把前面designer.exe的路径填到程序栏中,点击确定 然后设置为默认值,并点击确定 当在vs中配置好Qt环境之后,但…

【深度学习与大模型基础】第9章-条件概率以及条件概率的链式法则

简单理解条件概率 条件概率就是在已知某件事发生的情况下,另一件事发生的概率。用数学符号表示就是: P(A|B) 在B发生的前提下,A发生的概率。 计算机例子:垃圾邮件过滤 假设你写了一个程序来自动判断邮件是否是垃圾邮件&#xf…

STM32-FreeRTOS的详细配置

配置FreeRTOS 原文链接:https://ydamooc.github.io/posts/c9defcd/ 1.1 下载FreeRTOS 打开FreeRTOS官网:https://www.freertos.org/ 点击下载,并且选择"FreeRTOS 202212.01"版本,再点击Download按钮下载官方的资源包…

视觉自回归图像生成:基于多模态大模型的万字深度梳理

目前利用多模态大模型进行图像生成主要有以下两种形式: LLM作为condtioner:利用MLLM依据用户输入的text prompt来生成条件信息,条件信息被注入到下游生成模型进行更精细化的生成控制。这种形式通常需要外接一个额外专门的多模态生成模型&…

openssh离线一键升级脚本分享(含安装包)

查看当前的版本 [rootmyoracle ~]#ssh -V相关安装包下载地址 openssh下载地址:http://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssl下载地址:https://www.openssl.org/source/zlib下载地址:http://www.zlib.net/今天演示从7.4升级…

音视频之H.265/HEVC预测编码

H.265/HEVC系列文章: 1、音视频之H.265/HEVC编码框架及编码视频格式 2、音视频之H.265码流分析及解析 3、音视频之H.265/HEVC预测编码 预测编码是视频编码中的核心技术之一。对于视频信号来说,一幅图像内邻近像素之间有着较强的空间相关性,相邻图像之…

Python异步编程入门:Async/Await实战详解

引言 在当今高并发的应用场景下,传统的同步编程模式逐渐暴露出性能瓶颈。Python通过asyncio模块和async/await语法为开发者提供了原生的异步编程支持。本文将手把手带你理解异步编程的核心概念,并通过实际代码案例演示如何用异步爬虫提升10倍效率&#…

设计模式每日硬核训练 Day 13:桥接模式(Bridge Pattern)完整讲解与实战应用

🔄 回顾 Day 12:装饰器模式小结 在 Day 12 中,我们学习了装饰器模式(Decorator Pattern): 强调在不改变原类结构的前提下,动态为对象增强功能。通过“包装对象”实现运行时组合,支…

库洛游戏一面+二面

目录 一面 1. ArrayList和LinkedList的区别,就是我在插入和删除的时候他们在时间复杂度上有什么区别 2. hashmap在java的底层是怎么实现的 3. 红黑树的实现原理 4. 红黑树的特点 5. 为什么红黑树比链表查询速度快 6. 在java中字符串的操作方式有几种 7. Stri…

XSS攻击(反射型、存储型、dom型、PDF、SWF、SVG)

一、XSS攻击是什么 XSS是恶意攻击者往 Web 页面里插入恶意可执行网页脚本代码,当用户浏览该页之时,嵌入其中 Web 里面的脚本代码会被执行,从而可以达到攻击者盗取用户信息或其他侵犯用户安全隐私的目的。 二、XSS分类 反射型XSS 常见情况是…

C复习(主要复习)

指针和数组 指针数组是一个数组,数组的每个元素都是指针。它适用于需要存储多个指针的场景,如字符串数组。数组指针是一个指针,指向一个数组。它适用于需要传递整个数组给函数或处理多维数组的场景。 函数指针:函数指针的定义需要…

Ubuntu24.04搭建ESP8266_RTOS_SDK V3.4开发环境

【本文发布于https://blog.csdn.net/Stack_/article/details/147194686,未经允许不得转载,转载须注明出处】 需要有Linux使用基础,自行准备 1、VM17 Pro (自行搜索教程安装) 2、ubuntu-24.04-desktop-amd64 &#xff0…

matlab求和∑函数方程编程?

matlab求和∑函数方程编程? 一 题目:求下列函数方程式的和 二:代码如下: >> sum_result 0; % 初始化求和变量 for x 1:10 % 设…

基于Java+MySQL实现的(Web)科研资讯推送系统

科研资讯推送系统 技术选型 核心框架:SpringBoot 数据库层:springdatajpa 安全框架:Shiro 数据库连接池:Druid 缓存:Ehcache 部署 阿里云学生机:ecs.n4.small Tomcat:9.0 JDK:1.8 数据库:MySQL8.0 操作系统:CentOS…