Procházet zdrojové kódy

用户管理、角色管理bug问题修改

lingmin_package@163.com před 1 měsícem
rodič
revize
cf84e0bc21

+ 6 - 1
src/app/base/async_mysql_connection.py

@@ -74,9 +74,14 @@ def get_engine():
             database_url,
             echo=database_echo,
             pool_pre_ping=True,
-            pool_recycle=300,
+            pool_recycle=300,   # 每5分钟回收连接(合理)
             pool_size=10,
             max_overflow=20,
+            connect_args={
+                "connect_timeout": 10,
+                "read_timeout": 30,
+                "write_timeout": 30,
+            }
         )
         logger.info(f"SQLAlchemy 异步引擎已创建")
     

+ 73 - 19
src/app/services/system_service.py

@@ -370,12 +370,18 @@ class SystemService:
             offset = (page - 1) * page_size
             cursor.execute(f"""
                 SELECT r.id, r.code, r.name, r.description, r.is_active,
-                       r.is_system, r.created_time, r.updated_time,
-                       COUNT(ur.user_id) as user_count
+                       r.is_system, r.created_time, r.updated_time, r.created_by, r.updated_by,
+                       COUNT(ur.user_id) as user_count,
+                       creator.username as created_by_name,
+                       updater.username as updated_by_name
                 FROM t_sys_role r
                 LEFT JOIN t_sys_user_role ur ON r.id = ur.role_id AND ur.is_active = 1
+                LEFT JOIN t_sys_user creator ON r.created_by = creator.id
+                LEFT JOIN t_sys_user updater ON r.updated_by = updater.id
                 WHERE {where_clause}
-                GROUP BY r.id
+                GROUP BY r.id, r.code, r.name, r.description, r.is_active, r.is_system, 
+                         r.created_time, r.updated_time, r.created_by, r.updated_by,
+                         creator.username, updater.username
                 ORDER BY r.is_system DESC, r.created_time
                 LIMIT %s OFFSET %s
             """, params + [page_size, offset])
@@ -384,13 +390,17 @@ class SystemService:
             for row in cursor.fetchall():
                 role = {
                     "id": row['id'],
-                    "name": row['code'],
-                    "display_name": row['name'],
+                    "code": row['code'],
+                    "name": row['name'],
                     "description": row['description'],
                     "is_active": bool(row['is_active']),
                     "is_system": bool(row['is_system']),
                     "created_time": row['created_time'].isoformat() if row['created_time'] else None,
                     "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
+                    "created_by": row['created_by'],
+                    "updated_by": row['updated_by'],
+                    "created_by_name": row['created_by_name'],
+                    "updated_by_name": row['updated_by_name'],
                     "user_count": row['user_count']
                 }
                 roles.append(role)
@@ -454,7 +464,7 @@ class SystemService:
                 roles.append({
                     "id": row['id'],
                     "code": row['code'],
-                    "display_name": row['name'],
+                    "name": row['name'],
                     "is_system": bool(row['is_system']),
                     "is_active": bool(row['is_active'])
                 })
@@ -672,19 +682,24 @@ class SystemService:
             if is_super_admin_role:
                 return False, None, "超级管理员角色拥有全部权限,无需修改"
             
-            # 验证菜单ID是否存在
+            # 验证菜单ID是否存在并获取菜单信息
+            final_menu_ids = []
             if menu_ids:
                 placeholders = ','.join(['%s'] * len(menu_ids))
                 cursor.execute(f"""
-                    SELECT id FROM t_sys_menu 
+                    SELECT id, parent_id FROM t_sys_menu 
                     WHERE id IN ({placeholders}) AND is_active = 1
                 """, menu_ids)
                 
-                valid_menu_ids = [row['id'] for row in cursor.fetchall()]
+                valid_menus = cursor.fetchall()
+                valid_menu_ids = [menu['id'] for menu in valid_menus]
                 invalid_menu_ids = set(menu_ids) - set(valid_menu_ids)
                 
                 if invalid_menu_ids:
                     return False, None, f"无效的菜单ID: {', '.join(invalid_menu_ids)}"
+                
+                # 自动包含父菜单
+                final_menu_ids = self._include_parent_menus(cursor, valid_menu_ids)
             
             # 开始事务
             cursor.execute("START TRANSACTION")
@@ -692,14 +707,13 @@ class SystemService:
             try:
                 # 删除角色现有的菜单权限
                 cursor.execute("DELETE FROM t_sys_role_menu WHERE role_id = %s", (role_id,))
-                
-                # 添加新的菜单权限
-                if menu_ids:
-                    values = [(role_id, menu_id) for menu_id in menu_ids]
-                    cursor.executemany("""
-                        INSERT INTO t_sys_role_menu (role_id, menu_id, created_by, created_time)
-                        VALUES (%s, %s, %s, NOW())
-                    """, [(role_id, menu_id, updater_id) for menu_id in menu_ids])
+                # 添加新的菜单权限(包含自动添加的父菜单)
+                if final_menu_ids:
+                    for menu_id in final_menu_ids:
+                        cursor.execute("""
+                            INSERT INTO t_sys_role_menu (id, role_id, menu_id, created_by, created_time)
+                            VALUES (%s, %s, %s, %s, NOW())
+                        """, (str(uuid.uuid4()), role_id, menu_id, updater_id))
                 
                 # 提交事务
                 conn.commit()
@@ -707,8 +721,10 @@ class SystemService:
                 result = {
                     "role_id": role_id,
                     "role_name": role["name"],
-                    "menu_ids": menu_ids,
-                    "updated_count": len(menu_ids)
+                    "menu_ids": final_menu_ids,
+                    "updated_count": len(final_menu_ids),
+                    "original_count": len(menu_ids) if menu_ids else 0,
+                    "auto_added_parents": len(final_menu_ids) - len(menu_ids) if menu_ids else 0
                 }
                 
                 return True, result, "角色菜单权限更新成功"
@@ -723,6 +739,44 @@ class SystemService:
             cursor.close()
             conn.close()
     
+    def _include_parent_menus(self, cursor, menu_ids: List[str]) -> List[str]:
+        """自动包含父菜单,确保菜单层级完整"""
+        if not menu_ids:
+            return []
+        
+        # 获取所有相关菜单的信息
+        placeholders = ','.join(['%s'] * len(menu_ids))
+        cursor.execute(f"""
+            SELECT id, parent_id FROM t_sys_menu 
+            WHERE id IN ({placeholders}) AND is_active = 1
+        """, menu_ids)
+        
+        menus = cursor.fetchall()
+        result_menu_ids = set(menu_ids)
+        
+        # 递归添加父菜单
+        def add_parent_menu(menu_id: str):
+            # 查找父菜单
+            cursor.execute("""
+                SELECT parent_id FROM t_sys_menu 
+                WHERE id = %s AND is_active = 1
+            """, (menu_id,))
+            
+            parent_result = cursor.fetchone()
+            if parent_result and parent_result['parent_id']:
+                parent_id = parent_result['parent_id']
+                if parent_id not in result_menu_ids:
+                    result_menu_ids.add(parent_id)
+                    # 递归添加父菜单的父菜单
+                    add_parent_menu(parent_id)
+        
+        # 为每个选中的菜单添加其父菜单
+        for menu in menus:
+            if menu['parent_id']:
+                add_parent_menu(menu['id'])
+        
+        return list(result_menu_ids)
+    
     # ==================== 菜单管理 ====================
     
     async def create_menu(self, menu_data: Dict[str, Any], creator_id: str) -> Tuple[bool, str]:

+ 70 - 3
src/app/services/system_service_ext.py

@@ -28,6 +28,62 @@ class SystemServiceExt:
     
     # ==================== 用户管理 ====================
     
+    async def get_user_detail(self, user_id: str) -> Optional[Dict[str, Any]]:
+        """获取用户详情(包含角色信息)"""
+        conn = get_db_connection()
+        if not conn:
+            return None
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 查询用户基本信息和详情
+            cursor.execute("""
+                SELECT u.id, u.username, u.email, u.phone, u.is_active, u.is_superuser,
+                       u.last_login_at, u.created_time, up.real_name, up.company, up.department
+                FROM t_sys_user u
+                LEFT JOIN t_sys_user_profile up ON u.id = up.user_id
+                WHERE u.id = %s
+            """, (user_id,))
+            
+            user_data = cursor.fetchone()
+            if not user_data:
+                return None
+            
+            # 查询用户角色
+            cursor.execute("""
+                SELECT r.id, r.name, r.code
+                FROM t_sys_user_role ur
+                JOIN t_sys_role r ON ur.role_id = r.id
+                WHERE ur.user_id = %s AND ur.is_active = 1
+            """, (user_id,))
+            
+            roles = cursor.fetchall()
+            role_ids = [role['id'] for role in roles]
+            role_names = [role['name'] for role in roles]
+            
+            # 构建返回数据
+            user_detail = {
+                "id": user_data['id'],
+                "username": user_data['username'],
+                "email": user_data['email'],
+                "phone": user_data['phone'],
+                "is_active": bool(user_data['is_active']),
+                "is_superuser": bool(user_data['is_superuser']),
+                "last_login_at": user_data['last_login_at'].isoformat() if user_data['last_login_at'] else None,
+                "created_time": user_data['created_time'].isoformat() if user_data['created_time'] else None,
+                "real_name": user_data['real_name'],
+                "company": user_data['company'],
+                "department": user_data['department'],
+                "role_ids": role_ids,
+                "roles": ', '.join(role_names) if role_names else None
+            }
+            
+            return user_detail
+        finally:
+            cursor.close()
+            conn.close()
+    
     async def get_users(self, page: int, page_size: int, keyword: Optional[str] = None) -> Tuple[List[Dict[str, Any]], int]:
         """获取用户列表"""
         conn = get_db_connection()
@@ -55,15 +111,21 @@ class SystemServiceExt:
             offset = (page - 1) * page_size
             cursor.execute(f"""
                 SELECT u.id, u.username, u.email, u.phone, u.is_active, u.is_superuser,
-                       u.last_login_at, u.created_time, up.real_name, up.company, up.department,
-                       GROUP_CONCAT(r.name) as roles
+                       u.last_login_at, u.created_time, u.updated_time, u.created_by, u.updated_by,
+                       up.real_name, up.company, up.department,
+                       GROUP_CONCAT(r.name) as roles,
+                       creator.username as created_by_name,
+                       updater.username as updated_by_name
                 FROM t_sys_user u
                 LEFT JOIN t_sys_user_profile up ON u.id = up.user_id
                 LEFT JOIN t_sys_user_role ur ON u.id = ur.user_id AND ur.is_active = 1
                 LEFT JOIN t_sys_role r ON ur.role_id = r.id
+                LEFT JOIN t_sys_user creator ON u.created_by = creator.id
+                LEFT JOIN t_sys_user updater ON u.updated_by = updater.id
                 WHERE {where_clause}
                 GROUP BY u.id, u.username, u.email, u.phone, u.is_active, u.is_superuser,
-                         u.last_login_at, u.created_time, up.real_name, up.company, up.department
+                         u.last_login_at, u.created_time, u.updated_time, u.created_by, u.updated_by,
+                         up.real_name, up.company, up.department, creator.username, updater.username
                 ORDER BY u.created_time DESC
                 LIMIT %s OFFSET %s
             """, params + [page_size, offset])
@@ -79,6 +141,11 @@ class SystemServiceExt:
                     "is_superuser": bool(row['is_superuser']),
                     "last_login_at": row['last_login_at'].isoformat() if row['last_login_at'] else None,
                     "created_time": row['created_time'].isoformat() if row['created_time'] else None,
+                    "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
+                    "created_by": row['created_by'],
+                    "updated_by": row['updated_by'],
+                    "created_by_name": row['created_by_name'],
+                    "updated_by_name": row['updated_by_name'],
                     "real_name": row['real_name'],
                     "company": row['company'],
                     "department": row['department'],

+ 1 - 0
src/test/file/hello_world.txt

@@ -0,0 +1 @@
+test第三方第三方神鼎飞丹砂水电费sd

+ 161 - 0
src/test/test_minio.py

@@ -0,0 +1,161 @@
+import io
+from datetime import timedelta
+from minio import Minio
+from minio.error import S3Error
+
+# 1. 初始化客户端
+# 配置信息来源于用户最新提供
+ENDPOINT = "192.168.91.15:19000"
+ACCESS_KEY = "2I8pipSfCa7tgBzGEZQJ"
+SECRET_KEY = "468kSI3wKWix5ThlBOVuRRUst8ImudH3aSCyOwmQ"
+BUCKET_NAME = "auto"
+BASE_PATH = "sampledata"
+
+client = Minio(
+    ENDPOINT,
+    access_key=ACCESS_KEY,
+    secret_key=SECRET_KEY,
+    secure=False  # 使用 HTTP
+)
+
+def upload_file(file_path: str, content: bytes, content_type: str = "application/octet-stream"):
+    """
+    上传文件到 MinIO
+    Args:
+        file_path: 文件路径(相对于 sampledata/),如 "test/hello.txt"
+        content: 文件内容(bytes)
+        content_type: MIME 类型
+    """
+    object_name = f"{BASE_PATH}/{file_path}"
+    try:
+        result = client.put_object(
+            BUCKET_NAME,
+            object_name,
+            io.BytesIO(content),
+            length=len(content),
+            content_type=content_type
+        )
+        print(f"上传成功:{result.object_name}, ETag: {result.etag}")
+        return result
+    except S3Error as e:
+        print(f"上传失败: {e}")
+
+def download_file(file_path: str) -> bytes:
+    """
+    从 MinIO 下载文件
+    Args:
+        file_path: 文件路径(相对于 sampledata/)
+    Returns:
+        文件内容 (bytes)
+    """
+    object_name = f"{BASE_PATH}/{file_path}"
+    try:
+        response = client.get_object(BUCKET_NAME, object_name)
+        content = response.read()
+        response.close()
+        response.release_conn()
+        return content
+    except S3Error as e:
+        print(f"下载失败: {e}")
+        return b""
+
+def list_files(prefix: str = "", recursive: bool = True):
+    """
+    列出指定路径下的文件
+    Args:
+        prefix: 路径前缀 (相对于 sampledata/)
+        recursive: 是否递归列出子目录
+    """
+    full_prefix = f"{BASE_PATH}/{prefix}" if prefix else f"{BASE_PATH}/"
+    try:
+        objects = client.list_objects(BUCKET_NAME, prefix=full_prefix, recursive=recursive)
+        files = []
+        for obj in objects:
+            files.append({
+                "name": obj.object_name,
+                "size": obj.size,
+                "last_modified": obj.last_modified
+            })
+        return files
+    except S3Error as e:
+        print(f"获取文件列表失败: {e}")
+        return []
+
+def delete_file(file_path: str):
+    """
+    删除文件
+    Args:
+        file_path: 文件路径(相对于 sampledata/)
+    """
+    object_name = f"{BASE_PATH}/{file_path}"
+    try:
+        client.remove_object(BUCKET_NAME, object_name)
+        print(f"删除成功:{object_name}")
+    except S3Error as e:
+        print(f"删除失败: {e}")
+
+def get_presigned_url(file_path: str, expires: int = 3600):
+    """
+    生成预签名下载 URL
+    Args:
+        file_path: 文件路径(相对于 sampledata/)
+        expires: 过期时间(秒),默认1小时
+    """
+    object_name = f"{BASE_PATH}/{file_path}"
+    try:
+        url = client.presigned_get_object(
+            BUCKET_NAME,
+            object_name,
+            expires=timedelta(seconds=expires)
+        )
+        return url
+    except S3Error as e:
+        print(f"生成预签名URL失败: {e}")
+
+def get_presigned_upload_url(file_path: str, expires: int = 3600):
+    """
+    生成预签名上传 URL
+    Args:
+        file_path: 文件路径(相对于 sampledata/)
+        expires: 过期时间(秒),默认1小时
+    """
+    object_name = f"{BASE_PATH}/{file_path}"
+    try:
+        url = client.presigned_put_object(
+            BUCKET_NAME,
+            object_name,
+            expires=timedelta(seconds=expires)
+        )
+        return url
+    except S3Error as e:
+        print(f"生成上传URL失败: {e}")
+
+if __name__ == "__main__":
+    # 测试流程
+    test_filename = "file/hello_world.txt"
+    test_content = b"Hello, MinIO! This is a test file."
+
+    print("--- 1. 上传测试 ---")
+    upload_file(test_filename, test_content, "text/plain")
+
+    print("\n--- 2. 列表测试 ---")
+    files = list_files("test/")
+    for f in files:
+        print(f"文件: {f['name']},大小:{f['size']} bytes")
+
+    print("\n--- 3. 下载测试 ---")
+    content = download_file(test_filename)
+    print(f"下载内容: {content.decode('utf-8')}")
+
+    print("\n--- 4. 预签名 URL 测试 ---")
+    download_url = get_presigned_url(test_filename)
+    print(f"下载链接:{download_url}")
+    
+    upload_url = get_presigned_upload_url("test/upload_via_url.txt")
+    print(f"上传链接:{upload_url}")
+    print("(可使用 requests.put(upload_url, data=b'content') 测试上传)")
+
+    print("\n--- 5. 删除测试 ---")
+    delete_file(test_filename)
+    
+    print("\n测试完成。")

+ 36 - 336
src/views/system_view.py

@@ -333,15 +333,7 @@ async def api_get_all_menus(
                 timestamp=datetime.now(timezone.utc).isoformat()
             ).model_dump()
         
-        # 简化权限检查 - 只检查是否为管理员
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(
-                code=403,
-                message="权限不足",
-                timestamp=datetime.now(timezone.utc).isoformat()
-            ).model_dump()
-
+     
         # 调用 service 层
         system_service = SystemService()
         menus, total = await system_service.get_all_menus(page, page_size, keyword)
@@ -386,15 +378,6 @@ async def api_get_all_roles(
                 timestamp=datetime.now(timezone.utc).isoformat()
             ).model_dump()
         
-        # 简化权限检查 - 只检查是否为管理员
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(
-                code=403,
-                message="权限不足",
-                timestamp=datetime.now(timezone.utc).isoformat()
-            ).model_dump()
-
          # 调用 service 层
         system_service = SystemService()
         roles, total = await system_service.get_all_roles(page , page_size , keyword)
@@ -473,9 +456,6 @@ async def get_users(
         if not payload:
             return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         # 调用 service 层
         system_service_ext = SystemServiceExt()
@@ -531,6 +511,32 @@ async def create_user(
         return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 
+@router.get("/admin/users/{user_id}")
+async def get_user_detail(
+    user_id: str,
+    credentials: HTTPAuthorizationCredentials = Depends(security)
+):
+    """获取用户详情"""
+    try:
+        payload = verify_token(credentials.credentials)
+        if not payload:
+            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        
+        
+        # 调用 service 层获取用户详情
+        system_service_ext = SystemServiceExt()
+        user_detail = await system_service_ext.get_user_detail(user_id)
+        
+        if user_detail:
+            return ApiResponse(code=0, data=user_detail, message="Success", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        else:
+            return ApiResponse(code=404, message="用户不存在", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        
+    except Exception as e:
+        logger.exception("获取用户详情错误")
+        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+
+
 
 
 
@@ -546,14 +552,13 @@ async def update_user(
         if not payload:
             return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
         updater_id = payload.get("sub")
-         # 创建密码哈希
-        password_hash = hash_password_simple(user_data['password'])
-        user_data['password'] = password_hash
+        
+        # 只有当密码不为空时才进行哈希处理
+        if user_data.get('password'):
+            password_hash = hash_password_simple(user_data['password'])
+            user_data['password'] = password_hash
         
         # 调用 service 层
         system_service_ext = SystemServiceExt()
@@ -620,13 +625,10 @@ async def create_role(
         if not payload:
             return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
+        current_user_id = payload.get("sub")
         # 调用 service 层
         system_service = SystemService()
-        success, message = await system_service.create_role(role_data)
+        success, message = await system_service.create_role(role_data  , current_user_id)
         
         if success:
             return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
@@ -654,10 +656,10 @@ async def update_role(
         is_superuser = payload.get("is_superuser", False)
         if not is_superuser:
             return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
+        user_id = payload.get("sub")
         # 调用 service 层
         system_service = SystemService()
-        success, message = await system_service.update_role(role_id, role_data)
+        success, message = await system_service.update_role(role_id, role_data , user_id)
 
         if success:
             return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
@@ -715,14 +717,6 @@ async def get_role_menus(
                 timestamp=datetime.now(timezone.utc).isoformat()
             ).model_dump()
         
-        # 检查管理员权限
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(
-                code=403,
-                message="权限不足",
-                timestamp=datetime.now(timezone.utc).isoformat()
-            ).model_dump()
         
         # 调用 service 层
         system_service = SystemService()
@@ -767,14 +761,6 @@ async def update_role_menus(
                 timestamp=datetime.now(timezone.utc).isoformat()
             ).model_dump()
         
-        # 检查管理员权限
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(
-                code=403,
-                message="权限不足",
-                timestamp=datetime.now(timezone.utc).isoformat()
-            ).model_dump()
         
         # 获取请求数据
         body = await request.json()
@@ -1278,289 +1264,3 @@ def generate_random_string(length=32):
     import string
     alphabet = string.ascii_letters + string.digits
     return ''.join(secrets.choice(alphabet) for _ in range(length))
-
-### 2. 获取所有角色
-@router.get("/admin/roles")
-async def api_get_all_roles(
-    page: int = 1,
-    page_size: int = 20,
-    keyword: Optional[str] = None,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """获取所有角色"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service = SystemService()
-        roles, total = await system_service.get_all_roles(page, page_size, keyword)
-        
-        return ApiResponse(
-            code=0,
-            message="获取角色列表成功",
-            data={"items": roles, "total": total, "page": page, "page_size": page_size},
-            timestamp=datetime.now(timezone.utc).isoformat()
-        ).model_dump()
-        
-    except Exception as e:
-        logger.exception("获取角色列表错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-    
-
-
-### 5. 创建用户
-@router.post("/admin/users")
-async def create_user(
-    user_data: dict,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """创建用户"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 创建密码哈希
-        system_service = SystemService()
-        password_hash = system_service._hash_password(user_data['password'])
-        
-        # 调用 service 层
-        system_service_ext = SystemServiceExt()
-        success, message = await system_service_ext.create_user(user_data, password_hash, payload.get("sub"))
-        
-        if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        else:
-            return ApiResponse(code=400, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("创建用户错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-
-
-### 6. 更新用户
-@router.put("/admin/users/{user_id}")
-async def update_user(
-    user_id: str,
-    user_data: dict,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """更新用户"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service_ext = SystemServiceExt()
-        success, message = await system_service_ext.update_user(user_id, user_data, payload.get("sub"))
-        
-        if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        else:
-            return ApiResponse(code=400, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("更新用户错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-
-
-### 7. 删除用户
-@router.delete("/admin/users/{user_id}")
-async def delete_user(
-    user_id: str,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """删除用户"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service_ext = SystemServiceExt()
-        success, message = await system_service_ext.delete_user(user_id, payload.get("sub"))
-        
-        if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        else:
-            return ApiResponse(code=400, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("删除用户错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-
-
-### 8. 创建角色
-@router.post("/admin/roles")
-async def create_role(
-    role_data: dict,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """创建角色"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service = SystemService()
-        success, message = await system_service.create_role(role_data)
-        
-        if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        else:
-            return ApiResponse(code=400, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("创建角色错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-### 9. 获取角色菜单权限
-@router.get("/admin/roles/{role_id}/menus")
-async def get_role_menus(
-    role_id: str,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """获取角色的菜单权限"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service = SystemService()
-        success, data, message = await system_service.get_role_menus(role_id)
-        
-        if success:
-            return ApiResponse(code=0, message=message, data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        else:
-            return ApiResponse(code=404 if "不存在" in message else 500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("获取角色菜单权限错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-
-### 10. 更新角色菜单权限
-
-@router.put("/admin/roles/{role_id}/menus")
-async def update_role_menus(
-    role_id: str,
-    request: Request,
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """更新角色的菜单权限"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        is_superuser = payload.get("is_superuser", False)
-        if not is_superuser:
-            return ApiResponse(code=403, message="权限不足", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 获取请求数据
-        body = await request.json()
-        menu_ids = body.get("menu_ids", [])
-        
-        if not isinstance(menu_ids, list):
-            return ApiResponse(code=400, message="菜单ID列表格式错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service = SystemService()
-        success, data, message = await system_service.update_role_menus(role_id, menu_ids)
-        
-        if success:
-            return ApiResponse(code=0, message=message, data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        else:
-            return ApiResponse(code=400, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("更新角色菜单权限错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-### 11. 获取所有角色(简化版)
-
-@router.get("/roles/all")
-async def get_all_roles_simple(credentials: HTTPAuthorizationCredentials = Depends(security)):
-    """获取所有角色(简化版,用于下拉选择)"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=401, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        # 调用 service 层
-        system_service = SystemService()
-        roles = await system_service.get_all_roles_simple()
-        
-        return ApiResponse(code=0, message="获取角色列表成功", data=roles, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-    except Exception as e:
-        logger.exception("获取角色列表错误")
-        return ApiResponse(code=500, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-
-
-### 12. 获取应用列表
-@router.get("/apps")
-async def get_apps(
-    page: int = 1,
-    page_size: int = 20,
-    keyword: str = "",
-    status: str = "",
-    credentials: HTTPAuthorizationCredentials = Depends(security)
-):
-    """获取应用列表"""
-    try:
-        payload = verify_token(credentials.credentials)
-        if not payload:
-            return ApiResponse(code=200002, message="无效的访问令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
-        
-        user_id = payload.get("sub")
-        
-        # 检查用户角色
-        system_service_ext = SystemServiceExt()
-        is_app_manager = await system_service_ext.check_user_app_manager_role(user_id)
-        
-        # 调用 service 层
-        apps, total = await system_service_ext.get_apps(page, page_size, user_id, is_app_manager, keyword, status)
-        
-        return ApiResponse(
-            code=0,
-            message="获取应用列表成功",
-            data={"items": apps, "total": total, "page": page, "page_size": page_size},
-            timestamp=datetime.now(timezone.utc).isoformat()
-        ).model_dump()
-        
-    except Exception as e:
-        logger.exception("获取应用列表错误")
-        return ApiResponse(code=500001, message="服务器内部错误", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()

+ 1 - 0
项目/项目追加开发说明.md

@@ -89,6 +89,7 @@
     - 修改或编辑任何一个字段时 修改人 updated_by 获取当前登录用户ID
     - 修改或编辑任何一个字段时 修改时间 updated_time 获取当前系统时间
     - 
+  - 所有列表界面的创建人、修改人使用表(t_sys_user username字段)进行显示