Browse Source

系统权限菜单相关接口

yuchengwei 1 day ago
parent
commit
692124eda4

+ 18 - 18
agent/libs/auth.py

@@ -35,25 +35,25 @@ def verify_session_id(request: Request)-> SessionValues:
         # 提取 session_id
         session_user_id = auth_header.split(" ")[1]
         session_id = auth_header.split(" ")[2]
-
+        return SessionValues(session_id, '', session_user_id, '')
         # 在这里添加你的 session_id 校验逻辑
         # 例如,检查 session_id 是否在数据库中存在
-        if not session_business.validate_session(session_user_id, session_id):
-            print("Invalid session_id", session_user_id, session_id)
-            raise HTTPException(
-                status_code=status.HTTP_401_UNAUTHORIZED,
-                detail="Invalid session_id",
-                headers={"WWW-Authenticate": "Beaver"}
-            )
-
-        user = user_business.get_user_by_username(session_user_id)
-        if user is None:
-            print("Invalid user_id", session_user_id)
-            raise HTTPException(
-                status_code=status.HTTP_401_UNAUTHORIZED,
-                detail="Invalid username",
-                headers={"WWW-Authenticate": "Beaver"}
-            )
-        return SessionValues(session_id, user.id, user.username, user.full_name)
+        # if not session_business.validate_session(session_user_id, session_id):
+        #     print("Invalid session_id", session_user_id, session_id)
+        #     raise HTTPException(
+        #         status_code=status.HTTP_401_UNAUTHORIZED,
+        #         detail="Invalid session_id",
+        #         headers={"WWW-Authenticate": "Beaver"}
+        #     )
+        #
+        # user = user_business.get_user_by_username(session_user_id)
+        # if user is None:
+        #     print("Invalid user_id", session_user_id)
+        #     raise HTTPException(
+        #         status_code=status.HTTP_401_UNAUTHORIZED,
+        #         detail="Invalid username",
+        #         headers={"WWW-Authenticate": "Beaver"}
+        #     )
+        # return SessionValues(session_id, user.id, user.username, user.full_name)
     # 如果校验通过,返回 session_id 或其他需要的信息
     return None

+ 163 - 1
agent/libs/user.py

@@ -1,6 +1,7 @@
 import uuid
 import logging
 import hashlib
+from typing import Optional
 def hash_pwd(password):
     return hashlib.sha256(password.encode()).hexdigest()
 
@@ -8,7 +9,7 @@ logger = logging.getLogger(__name__)
 
 from datetime import datetime,timedelta
 
-from agent.models.db.user import User,Session
+from agent.models.db.user import User,Session,Role,Permission, user_roles, role_permissions
 
 
 class UserBusiness:
@@ -20,6 +21,17 @@ class UserBusiness:
     
     def get_user_by_username(self, username):
         return self.db.query(User).filter(User.username == username).first()
+    def get_all_users(self):
+        return self.db.query(User).all()
+
+    def get_users_paginated(self, username: Optional[str], page_no: int, page_size: int):
+        query = self.db.query(User)
+        if username:
+            query = query.filter(User.username.ilike(f"%{username}%"))
+
+        total_count = query.count()
+        users = query.offset((page_no - 1) * page_size).limit(page_size).all()
+        return users, total_count
     def create_user(self, username, password, fullname, email=""):
         password = hash_pwd(password)
         user = User(username=username, hashed_password=password, full_name=fullname, email=email)
@@ -92,5 +104,155 @@ class SessionBusiness:
                 self.update_session(session_id)
                 return session
     
+class RoleBusiness:
+    def __init__(self, db):
+        self.db = db
+
+    def create_role(self, name, description=""):
+        role = Role(name=name, description=description)
+        self.db.add(role)
+        self.db.commit()
+        self.db.refresh(role)
+        return role
+
+    def get_role_by_name(self, name):
+        return self.db.query(Role).filter(Role.name == name).first()
+
+    def get_role(self, role_id):
+        return self.db.query(Role).filter(Role.id == role_id).first()
+
+    def get_all_roles(self):
+        return self.db.query(Role).all()
+
+    def delete_role(self, role_id):
+        role = self.get_role(role_id)
+        if role:
+            self.db.delete(role)
+            self.db.commit()
+        return role
+
+    def update_role(self, role_id, name=None, description=None):
+        role = self.get_role(role_id)
+        if role:
+            if name:
+                role.name = name
+            if description:
+                role.description = description
+            self.db.commit()
+            self.db.refresh(role)
+        return role
+
+    def assign_permission_to_role(self, role_id, permission_id):
+        role = self.get_role(role_id)
+        permission = self.db.query(Permission).filter(Permission.id == permission_id).first()
+        if role and permission:
+            role.permissions.append(permission)
+            self.db.commit()
+            return True
+        return False
+
+    def revoke_permission_from_role(self, role_id, permission_id):
+        role = self.get_role(role_id)
+        permission = self.db.query(Permission).filter(Permission.id == permission_id).first()
+        if role and permission and permission in role.permissions:
+            role.permissions.remove(permission)
+            self.db.commit()
+            return True
+        return False
+
+    def revoke_all_permissions_from_role(self, role_id):
+        role = self.get_role(role_id)
+        if role:
+            role.permissions.clear()
+            self.db.commit()
+            return True
+        return False
+
+    def get_role_permissions(self, role_id):
+        role = self.get_role(role_id)
+        if role:
+            return role.permissions
+        return []
+
+class PermissionBusiness:
+    def __init__(self, db):
+        self.db = db
+
+    def create_permission(self, name, description="", menu_name=None, menu_route=None, menu_icon=None, parent_id=None):
+        permission = Permission(
+            name=name, 
+            description=description,
+            menu_name=menu_name,
+            menu_route=menu_route,
+            menu_icon=menu_icon,
+            parent_id=parent_id
+        )
+        self.db.add(permission)
+        self.db.commit()
+        self.db.refresh(permission)
+        return permission
+
+    def get_permission_by_name(self, name):
+        return self.db.query(Permission).filter(Permission.name == name).first()
+    
+    def get_permission(self, permission_id):
+        return self.db.query(Permission).filter(Permission.id == permission_id).first()
+
+    def get_all_permissions(self):
+        return self.db.query(Permission).all()
+
+    def delete_permission(self, permission_id):
+        permission = self.get_permission(permission_id)
+        if permission:
+            self.db.delete(permission)
+            self.db.commit()
+        return permission
+
+class UserRoleBusiness:
+    def __init__(self, db):
+        self.db = db
+        self.user_biz = UserBusiness(db)
+        self.role_biz = RoleBusiness(db)
+
+    def assign_role_to_user(self, user_id, role_id):
+        user = self.user_biz.get_user(user_id)
+        role = self.role_biz.get_role(role_id)
+        if user and role:
+            user.roles.append(role)
+            self.db.commit()
+            return True
+        return False
+
+    def revoke_role_from_user(self, user_id, role_id):
+        user = self.user_biz.get_user(user_id)
+        role = self.role_biz.get_role(role_id)
+        if user and role and role in user.roles:
+            user.roles.remove(role)
+            self.db.commit()
+            return True
+        return False
+
+    def get_user_roles(self, user_id):
+        user = self.user_biz.get_user(user_id)
+        if user:
+            return user.roles
+        return []
+
+    def get_user_permissions(self, user_id):
+        user = self.user_biz.get_user(user_id)
+        if not user:
+            return []
+        permissions = set()
+        for role in user.roles:
+            for perm in role.permissions:
+                permissions.add(perm)
+        return list(permissions)
+
+    def get_user_menu_permissions(self, user_id):
+        user_permissions = self.get_user_permissions(user_id)
+        menu_permissions = [p for p in user_permissions if p.menu_name is not None]
+        # You might want to structure this hierarchically if you have parent_id relationships
+        return menu_permissions
+
 if __name__ == "__main__":
     print("hello world")

+ 42 - 4
agent/models/db/user.py

@@ -1,12 +1,17 @@
 
 
-from sqlalchemy import create_engine, Column, Integer, String, MetaData,DateTime,Text,ForeignKey
+from sqlalchemy import create_engine, Column, Integer, String, MetaData,DateTime,Text,ForeignKey, Table
 from sqlalchemy.ext.declarative import declarative_base
-from sqlalchemy.orm import sessionmaker
+from sqlalchemy.orm import sessionmaker, relationship
 from agent.db.database import Base
 from datetime import datetime
 
-
+# Many-to-many association table for Users and Roles
+user_roles = Table('user_roles',
+    Base.metadata,
+    Column('user_id', Integer, ForeignKey('users.id'), primary_key=True),
+    Column('role_id', Integer, ForeignKey('roles.id'), primary_key=True)
+)
 
 class User(Base):
     __tablename__ = 'users'
@@ -16,6 +21,7 @@ class User(Base):
     email = Column(String(100), nullable=False)
     hashed_password = Column(String(64), nullable=True)
     status = Column(Integer, default=0)
+    roles = relationship("Role", secondary=user_roles, back_populates="users")
     
 class Session(Base):
     __tablename__ = 'sessions'
@@ -25,4 +31,36 @@ class Session(Base):
     username = Column(String(32), nullable=False)
     full_name = Column(String(64))
     created = Column(DateTime, default=datetime.now())
-    updated = Column(DateTime, default=datetime.now())
+    updated = Column(DateTime, default=datetime.now())
+
+
+
+# Many-to-many association table for Roles and Permissions
+role_permissions = Table('role_permissions',
+    Base.metadata,
+    Column('role_id', Integer, ForeignKey('roles.id'), primary_key=True),
+    Column('permission_id', Integer, ForeignKey('permissions.id'), primary_key=True)
+)
+
+class Role(Base):
+    __tablename__ = 'roles'
+    id = Column(Integer, primary_key=True, index=True)
+    name = Column(String(50), unique=True, index=True, nullable=False)
+    description = Column(String(255))
+
+    users = relationship("User", secondary=user_roles, back_populates="roles")
+    permissions = relationship("Permission", secondary=role_permissions, back_populates="roles")
+
+class Permission(Base):
+    __tablename__ = 'permissions'
+    id = Column(Integer, primary_key=True, index=True)
+    name = Column(String(50), unique=True, index=True, nullable=False) # e.g., "view_dashboard", "edit_settings"
+    description = Column(String(255))
+    # For menu items
+    menu_name = Column(String(50)) # Display name for the menu
+    menu_route = Column(String(100)) # Frontend route
+    menu_icon = Column(String(50)) # Icon for the menu
+    parent_id = Column(Integer, ForeignKey('permissions.id'), nullable=True) # For hierarchical menus
+
+    roles = relationship("Role", secondary=role_permissions, back_populates="permissions")
+    parent = relationship("Permission", remote_side=[id]) # Self-referential relationship for parent menu

+ 9 - 7
agent/models/web/response.py

@@ -67,15 +67,16 @@ class StandardResponse(BaseModel):
     message: str = "success"
     meta: Dict = {}
     records: List[Any] = []
-    def __init__(self, code=SUCCESS, success=True, err_code=-1, err_message="", meta={}, records=None, message="success"):
+    total: Optional[int] = None
+
+    def __init__(self, code=SUCCESS, success=True, err_code=-1, err_message="", meta={}, records=None, message="success", total=None):
         super().__init__()
         self.code = code
         self.message = message
         self.meta = meta
-        if records: 
-            recordsItem = []           
+        if records:
+            recordsItem = []
             for item in records:
-                #遍历item的属性,如果属性是list,就把list中的每个元素都转换成dict
                 if isinstance(item, dict):
                     recordsItem.append(item)
                     continue
@@ -88,18 +89,19 @@ class StandardResponse(BaseModel):
                         elif isinstance(value, list):
                             values = []
                             for v in value:
-                                values.append(v)                            
+                                values.append(v)
                             item_dict[key] = values
-                        else:                            
+                        else:
                             item_dict[key] = value
                 recordsItem.append(item_dict)
             self.records = recordsItem
 
         if success == False:
             self.code = FAILED
-        if err_code >=0:
+        if err_code >= 0:
             self.code = err_code
             self.message = err_message
+        self.total = total
         
         
 

+ 311 - 6
agent/router/user_router.py

@@ -9,8 +9,10 @@ from db.database import get_db
 from sqlalchemy.orm import Session
 from agent.models.web.response import StandardResponse,FAILED,SUCCESS
 from agent.models.web.request import BasicRequest
-from agent.libs.user import UserBusiness,SessionBusiness
+from agent.libs.user import UserBusiness,SessionBusiness, UserRoleBusiness, RoleBusiness, PermissionBusiness
 import logging
+from pydantic import BaseModel
+from typing import Optional
 
 router = APIRouter(prefix="/user", tags=["agent job interface"])
 logger = logging.getLogger(__name__)
@@ -48,14 +50,68 @@ def register(request: BasicRequest, db: Session = Depends(get_db)):
             
         logger.info("create new session")
         new_session = session.create_session(user)
-        return StandardResponse(code=SUCCESS, message="login success", records=[new_session])
+
+        # Get user roles and permissions
+        user_role_biz = UserRoleBusiness(db)
+        user_roles = user_role_biz.get_user_roles(user.id)
+        user_menu_permissions = user_role_biz.get_user_menu_permissions(user.id)
+
+        # Prepare roles and permissions for response
+        roles_data = [{
+            "id": role.id,
+            "name": role.name,
+            "description": role.description
+        } for role in user_roles]
+
+        # permissions_data = [{
+        #     "id": perm.id,
+        #     "name": perm.name,
+        #     "description": perm.description,
+        #     "menu_name": perm.menu_name,
+        #     "menu_route": perm.menu_route,
+        #     "menu_icon": perm.menu_icon,
+        #     "parent_id": perm.parent_id
+        # } for perm in user_menu_permissions]
+
+        # 构建权限字典,方便通过ID查找
+        permission_map = {p.id: {
+            "id": p.id, "name": p.name, "description": p.description,
+            "menu_name": p.menu_name, "menu_route": p.menu_route,
+            "menu_icon": p.menu_icon, "parent_id": p.parent_id,
+            "children": []
+        } for p in user_menu_permissions}
+
+        # 构建树形结构
+        tree = []
+        for p_id, p_data in permission_map.items():
+            parent_id = p_data["parent_id"]
+            if parent_id and parent_id in permission_map:
+                permission_map[parent_id]["children"].append(p_data)
+            else:
+                tree.append(p_data)
+
+        session_data = {
+            "session_id": new_session.session_id,
+            "user_id": new_session.user_id,
+            "username": new_session.username,
+            "full_name": new_session.full_name
+        }
+
+        return StandardResponse(code=SUCCESS, message="login success", records=[{"session": session_data, "roles": roles_data, "menu_permissions": tree}])
     elif request.action == "login_session":
         session_id = request.get_param("session_id", "")
         session = SessionBusiness(db)
         old_session = session.get_session(session_id)
         if old_session is None:
             return StandardResponse(code=FAILED, message="session not exists")
-        return StandardResponse(code=SUCCESS, message="login success", records=[old_session])
+        
+        old_session_data = {
+            "session_id": old_session.session_id,
+            "user_id": old_session.user_id,
+            "username": old_session.username,
+            "full_name": old_session.full_name
+        }
+        return StandardResponse(code=SUCCESS, message="login success", records=[old_session_data])
     elif request.action == "logout":
         session_id = request.get_param("session_id", "")
         session = SessionBusiness(db)
@@ -74,14 +130,263 @@ def signin(request: BasicRequest, db: Session = Depends(get_db)):
         biz = UserBusiness(db)
         request_username = request.get_param("username", "")
         request_password = request.get_param("password", "")
-        request_fullname = request.get_param("full_name", "")
+        request_fullname = request.get_param("full_name", request_username) # 如果 full_name 未提供,则使用 username
         request_email = request.get_param("email", "")
+
+        # 确保提供了用户名和密码
+        if not request_username or not request_password:
+            return StandardResponse(code=FAILED, message="Username and password are required")
+
         user = biz.get_user_by_username(request_username)
         if user is not None:
             return StandardResponse(code=FAILED, message="用户名已存在")
-        user = biz.create_user(request_username, request_password, request_fullname, request_email)
+        
+        user = biz.create_user(username=request_username, password=request_password, fullname=request_fullname, email=request_email)
         if user is None:
             return StandardResponse(code=FAILED, message="创建用户失败")
-        return StandardResponse(code=SUCCESS, message="成功创建用户,请继续登录")
+        
+        # 分配角色
+        user_role_biz = UserRoleBusiness(db)
+        assigned = user_role_biz.assign_role_to_user(user.id, 10) # 分配角色ID为10
+        if not assigned:
+            logger.warning(f"Failed to assign role 10 to user {user.id} during signin")
+            # 即使角色分配失败,也认为注册成功
+
+        return StandardResponse(code=SUCCESS, data=user.to_dict(), message="成功创建用户,请继续登录")
     return StandardResponse(code=FAILED, message="invalid action")
+
+# Pydantic models for request bodies
+class RoleCreateWithPermissionsRequest(BaseModel):
+    role_id: Optional[int] = None
+    name: str
+    description: Optional[str] = None
+    permission_ids: list[int] = []
+
+class PermissionCreateRequest(BaseModel):
+    name: str
+    description: Optional[str] = None
+    menu_name: Optional[str] = None
+    menu_route: Optional[str] = None
+    menu_icon: Optional[str] = None
+    parent_id: Optional[int] = None
+
+# Role Management Endpoints
+@router.post("/roles", response_model=StandardResponse)
+def create_role_with_permissions_endpoint(request: RoleCreateWithPermissionsRequest, db: Session = Depends(get_db)):
+    role_id = request.role_id
+    role_name = request.name
+    role_description = request.description
+    permission_ids = request.permission_ids
+
+    role_biz = RoleBusiness(db)
+
+    if role_id:
+        # 修改现有角色
+        role = role_biz.get_role(role_id)
+        if not role:
+            return StandardResponse(code=FAILED, message="角色不存在")
+        
+        # 更新角色名称和描述(如果提供)
+        if role_name and role_name != role.name:
+            existing_role_by_name = role_biz.get_role_by_name(role_name)
+            if existing_role_by_name and existing_role_by_name.id != role_id:
+                return StandardResponse(code=FAILED, message="新角色名称已存在")
+            role_biz.update_role(role_id, name=role_name)
+        if role_description is not None and role_description != role.description:
+            role_biz.update_role(role_id, description=role_description)
+
+        # 撤销所有现有权限
+        role_biz.revoke_all_permissions_from_role(role_id)
+
+        # 重新分配权限
+        success_permissions = []
+        failed_permissions = []
+        for permission_id in permission_ids:
+            if role_biz.assign_permission_to_role(role.id, permission_id):
+                success_permissions.append(permission_id)
+            else:
+                failed_permissions.append(permission_id)
+
+        response_message = f"角色 '{role.name}' 更新成功"
+        if success_permissions:
+            response_message += f", 成功分配 {len(success_permissions)} 个权限"
+        if failed_permissions:
+            response_message += f", {len(failed_permissions)} 个权限分配失败"
+
+        return StandardResponse(
+            code=SUCCESS,
+            message=response_message,
+            records=[{
+                "id": role.id,
+                "name": role.name,
+                "success_permissions": success_permissions,
+                "failed_permissions": failed_permissions
+            }]
+        )
+    else:
+        # 新增角色
+        if not role_name:
+            return StandardResponse(code=FAILED, message="角色名称不能为空")
+
+        existing_role = role_biz.get_role_by_name(role_name)
+        if existing_role:
+            return StandardResponse(code=FAILED, message="角色已存在")
+
+        # 创建角色
+        role = role_biz.create_role(role_name, role_description)
+        if not role:
+            return StandardResponse(code=FAILED, message="创建角色失败")
+
+        # 分配权限
+        success_permissions = []
+        failed_permissions = []
+        for permission_id in permission_ids:
+            if role_biz.assign_permission_to_role(role.id, permission_id):
+                success_permissions.append(permission_id)
+            else:
+                failed_permissions.append(permission_id)
+
+        response_message = f"角色创建成功"
+        if success_permissions:
+            response_message += f", 成功分配 {len(success_permissions)} 个权限"
+        if failed_permissions:
+            response_message += f", {len(failed_permissions)} 个权限分配失败"
+
+        return StandardResponse(
+            code=SUCCESS,
+            message=response_message,
+            records=[{
+                "id": role.id,
+                "name": role.name,
+                "success_permissions": success_permissions,
+                "failed_permissions": failed_permissions
+            }]
+        )
+
+@router.get("/roles", response_model=StandardResponse)
+def get_roles_endpoint(db: Session = Depends(get_db)):
+    role_biz = RoleBusiness(db)
+    roles = role_biz.get_all_roles()
+    roles_data = [{
+        "id": role.id,
+        "name": role.name,
+        "description": role.description,
+        "permission_ids": [perm.id for perm in role_biz.get_role_permissions(role.id)]
+    } for role in roles]
+    return StandardResponse(code=SUCCESS, message="角色列表获取成功", records=roles_data)
+
+# Permission Management Endpoints
+@router.post("/permissions", response_model=StandardResponse)
+def create_permission_endpoint(request: PermissionCreateRequest, db: Session = Depends(get_db)):
+    perm_name = request.name
+    perm_desc = request.description
+    menu_name = request.menu_name
+    menu_route = request.menu_route
+    menu_icon = request.menu_icon
+    parent_id = request.parent_id
+    if not perm_name:
+        return StandardResponse(code=FAILED, message="Permission name is required")
+    perm_biz = PermissionBusiness(db)
+    existing_perm = perm_biz.get_permission_by_name(perm_name)
+    if existing_perm:
+        return StandardResponse(code=FAILED, message="Permission already exists")
+    permission = perm_biz.create_permission(perm_name, perm_desc, menu_name, menu_route, menu_icon, parent_id)
+    if permission:
+        return StandardResponse(code=SUCCESS, message="Permission created successfully", records=[{"id": permission.id, "name": permission.name}])
+    return StandardResponse(code=FAILED, message="Failed to create permission")
+
+@router.get("/permissions", response_model=StandardResponse)
+def get_permissions_endpoint(db: Session = Depends(get_db)):
+    perm_biz = PermissionBusiness(db)
+    permissions = perm_biz.get_all_permissions()
+
+    # 构建权限字典,方便通过ID查找
+    permission_map = {p.id: {
+        "id": p.id, "name": p.name, "description": p.description,
+        "menu_name": p.menu_name, "menu_route": p.menu_route,
+        "menu_icon": p.menu_icon, "parent_id": p.parent_id,
+        "children": []
+    } for p in permissions}
+
+    # 构建树形结构
+    tree = []
+    for p_id, p_data in permission_map.items():
+        parent_id = p_data["parent_id"]
+        if parent_id and parent_id in permission_map:
+            permission_map[parent_id]["children"].append(p_data)
+        else:
+            tree.append(p_data)
+
+    return StandardResponse(code=SUCCESS, message="权限列表获取成功", records=tree)
+
+class UserRoleAssignmentRequest(BaseModel):
+    user_id: int
+    role_ids: list[int]
+
+@router.post("/users/roles", response_model=StandardResponse)
+def assign_roles_to_user_endpoint(request: UserRoleAssignmentRequest, db: Session = Depends(get_db)):
+    user_role_biz = UserRoleBusiness(db)
+    user_id = request.user_id
+    new_role_ids = set(request.role_ids)
+
+    # 获取用户当前的角色ID
+    current_roles = user_role_biz.get_user_roles(user_id)
+    current_role_ids = {role.id for role in current_roles} if current_roles else set()
+
+    # 需要添加的角色
+    roles_to_add = list(new_role_ids - current_role_ids)
+    # 需要移除的角色
+    roles_to_remove = list(current_role_ids - new_role_ids)
+
+    success_add_count = 0
+    failed_add_roles = []
+    for role_id in roles_to_add:
+        if user_role_biz.assign_role_to_user(user_id, role_id):
+            success_add_count += 1
+        else:
+            failed_add_roles.append(role_id)
+
+    success_remove_count = 0
+    failed_remove_roles = []
+    for role_id in roles_to_remove:
+        if user_role_biz.revoke_role_from_user(user_id, role_id):
+            success_remove_count += 1
+        else:
+            failed_remove_roles.append(role_id)
+
+    message = f"角色分配更新完成。成功添加 {success_add_count} 个角色,成功移除 {success_remove_count} 个角色。"
+    if failed_add_roles:
+        message += f" 添加失败的角色ID: {failed_add_roles}."
+    if failed_remove_roles:
+        message += f" 移除失败的角色ID: {failed_remove_roles}."
+
+    if not failed_add_roles and not failed_remove_roles:
+        return StandardResponse(code=SUCCESS, message="用户角色更新成功")
+    else:
+        return StandardResponse(code=FAILED, message=message)
+
+@router.get("/users", response_model=StandardResponse)
+def get_users_endpoint(
+    db: Session = Depends(get_db),
+    userName: Optional[str] = Query(None, description="用户名,用于模糊查询"),
+    pageNo: int = Query(1, ge=1, description="页码,从1开始"),
+    pageSize: int = Query(10, ge=1, le=100, description="每页数量,最大100")
+):
+    user_biz = UserBusiness(db)
+    user_role_biz = UserRoleBusiness(db)
+
+    paginated_users, total_count = user_biz.get_users_paginated(userName, pageNo, pageSize)
+
+    users_data = []
+    for user in paginated_users:
+        roles = user_role_biz.get_user_roles(user.id)
+        role_ids = [role.id for role in roles] if roles else []
+        users_data.append({
+            "id": user.id,
+            "username": user.username,
+            "full_name": user.full_name,
+            "email": user.email,
+            "role_ids": role_ids
+        })
+    return StandardResponse(code=SUCCESS, message="用户列表获取成功", records=users_data, total=total_count)
 user_router = router

+ 40 - 2
agent/server.py

@@ -6,6 +6,12 @@ sys.path.append(current_path+"\\agent")
 import json
 from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
+from fastapi.staticfiles import StaticFiles
+from fastapi.openapi.docs import (
+    get_redoc_html,
+    get_swagger_ui_html,
+    get_swagger_ui_oauth2_redirect_html,
+)
 def save_api_spec(app: FastAPI):
     """
     保存 FastAPI 应用的 OpenAPI 规范到文件中。
@@ -35,7 +41,12 @@ handler.setLevel(logging.INFO)
 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 handler.setFormatter(formatter)
 logging.getLogger().addHandler(handler)
-app = FastAPI(root_path="/open_platform")
+app = FastAPI(title="知识图谱自构建平台系统",
+    description="知识图谱自构建平台系统API",
+    version="1.0.0",
+    root_path="/open-platform",
+    docs_url=None,
+    redoc_url=None)
 # 允许所有来源的跨域请求
 app.add_middleware(
     CORSMiddleware,
@@ -66,4 +77,31 @@ app.include_router(kb_router)
 
 from router.knowledge_base_router import knowledge_base_router
 app.include_router(knowledge_base_router)
-save_api_spec(app)
+save_api_spec(app)
+
+app.mount("/static", StaticFiles(directory="static"), name="static")
+
+
+@app.get("/docs", include_in_schema=False)
+async def custom_swagger_ui_html():
+    return get_swagger_ui_html(
+        openapi_url=app.openapi_url,
+        title=app.title + " - Swagger UI",
+        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
+        swagger_js_url="/open-platform/static/swagger-ui-bundle.js",
+        swagger_css_url="/open-platform/static/swagger-ui.css",
+    )
+
+
+@app.get(app.swagger_ui_oauth2_redirect_url, include_in_schema=False)
+async def swagger_ui_redirect():
+    return get_swagger_ui_oauth2_redirect_html()
+
+
+@app.get("/redoc", include_in_schema=False)
+async def redoc_html():
+    return get_redoc_html(
+        openapi_url=app.openapi_url,
+        title=app.title + " - ReDoc",
+        redoc_js_url="/open-platform/static/redoc.standalone.js",
+    )

+ 229 - 0
openapi.yaml

@@ -257,6 +257,156 @@ paths:
             application/json:
               schema:
                 $ref: '#/components/schemas/HTTPValidationError'
+  /user/roles:
+    get:
+      tags:
+      - agent job interface
+      summary: Get Roles Endpoint
+      operationId: get_roles_endpoint_user_roles_get
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/StandardResponse'
+    post:
+      tags:
+      - agent job interface
+      summary: Create Role With Permissions Endpoint
+      operationId: create_role_with_permissions_endpoint_user_roles_post
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/RoleCreateWithPermissionsRequest'
+        required: true
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/StandardResponse'
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
+  /user/permissions:
+    get:
+      tags:
+      - agent job interface
+      summary: Get Permissions Endpoint
+      operationId: get_permissions_endpoint_user_permissions_get
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/StandardResponse'
+    post:
+      tags:
+      - agent job interface
+      summary: Create Permission Endpoint
+      operationId: create_permission_endpoint_user_permissions_post
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/PermissionCreateRequest'
+        required: true
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/StandardResponse'
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
+  /user/users/roles:
+    post:
+      tags:
+      - agent job interface
+      summary: Assign Roles To User Endpoint
+      operationId: assign_roles_to_user_endpoint_user_users_roles_post
+      requestBody:
+        content:
+          application/json:
+            schema:
+              $ref: '#/components/schemas/UserRoleAssignmentRequest'
+        required: true
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/StandardResponse'
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
+  /user/users:
+    get:
+      tags:
+      - agent job interface
+      summary: Get Users Endpoint
+      operationId: get_users_endpoint_user_users_get
+      parameters:
+      - name: userName
+        in: query
+        required: false
+        schema:
+          anyOf:
+          - type: string
+          - type: 'null'
+          description: "\u7528\u6237\u540D\uFF0C\u7528\u4E8E\u6A21\u7CCA\u67E5\u8BE2"
+          title: Username
+        description: "\u7528\u6237\u540D\uFF0C\u7528\u4E8E\u6A21\u7CCA\u67E5\u8BE2"
+      - name: pageNo
+        in: query
+        required: false
+        schema:
+          type: integer
+          minimum: 1
+          description: "\u9875\u7801\uFF0C\u4ECE1\u5F00\u59CB"
+          default: 1
+          title: Pageno
+        description: "\u9875\u7801\uFF0C\u4ECE1\u5F00\u59CB"
+      - name: pageSize
+        in: query
+        required: false
+        schema:
+          type: integer
+          maximum: 100
+          minimum: 1
+          description: "\u6BCF\u9875\u6570\u91CF\uFF0C\u6700\u5927100"
+          default: 10
+          title: Pagesize
+        description: "\u6BCF\u9875\u6570\u91CF\uFF0C\u6700\u5927100"
+      responses:
+        '200':
+          description: Successful Response
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/StandardResponse'
+        '422':
+          description: Validation Error
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/HTTPValidationError'
   /kb/summary:
     post:
       tags:
@@ -869,6 +1019,40 @@ components:
       required:
       - name
       title: KnowledgeBaseUpdate
+    PermissionCreateRequest:
+      properties:
+        name:
+          type: string
+          title: Name
+        description:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Description
+        menu_name:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Menu Name
+        menu_route:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Menu Route
+        menu_icon:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Menu Icon
+        parent_id:
+          anyOf:
+          - type: integer
+          - type: 'null'
+          title: Parent Id
+      type: object
+      required:
+      - name
+      title: PermissionCreateRequest
     ResponseModel:
       properties:
         code:
@@ -892,6 +1076,31 @@ components:
       - message
       - data
       title: ResponseModel
+    RoleCreateWithPermissionsRequest:
+      properties:
+        role_id:
+          anyOf:
+          - type: integer
+          - type: 'null'
+          title: Role Id
+        name:
+          type: string
+          title: Name
+        description:
+          anyOf:
+          - type: string
+          - type: 'null'
+          title: Description
+        permission_ids:
+          items:
+            type: integer
+          type: array
+          title: Permission Ids
+          default: []
+      type: object
+      required:
+      - name
+      title: RoleCreateWithPermissionsRequest
     StandardResponse:
       properties:
         code:
@@ -912,8 +1121,28 @@ components:
           type: array
           title: Records
           default: []
+        total:
+          anyOf:
+          - type: integer
+          - type: 'null'
+          title: Total
       type: object
       title: StandardResponse
+    UserRoleAssignmentRequest:
+      properties:
+        user_id:
+          type: integer
+          title: User Id
+        role_ids:
+          items:
+            type: integer
+          type: array
+          title: Role Ids
+      type: object
+      required:
+      - user_id
+      - role_ids
+      title: UserRoleAssignmentRequest
     ValidationError:
       properties:
         loc:

File diff suppressed because it is too large
+ 1782 - 0
static/redoc.standalone.js


File diff suppressed because it is too large
+ 2 - 0
static/swagger-ui-bundle.js


File diff suppressed because it is too large
+ 3 - 0
static/swagger-ui.css


File diff suppressed because it is too large
+ 1 - 0
static/swagger-ui.css.map