style: normalize python formatting gates
This commit is contained in:
@@ -5,6 +5,4 @@ package is kept only as a migration marker; do not import it in application,
|
||||
API, worker, or new tests.
|
||||
"""
|
||||
|
||||
raise RuntimeError(
|
||||
"packages.adapters.postgres is deprecated; use packages.adapters.sqlalchemy_impl instead"
|
||||
)
|
||||
raise RuntimeError("packages.adapters.postgres is deprecated; use packages.adapters.sqlalchemy_impl instead")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""
|
||||
Asset PostgreSQL Repository 实现
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from sqlalchemy import and_, func, select
|
||||
@@ -24,7 +25,7 @@ class PostgresAssetRepository(AssetRepository):
|
||||
project_id=asset.project_id,
|
||||
asset_library_id=asset.library_id,
|
||||
name=asset.name,
|
||||
file_type=asset.mime_type.split("/")[0] if "/" in asset.mime_type else asset.mime_type,
|
||||
file_type=(asset.mime_type.split("/")[0] if "/" in asset.mime_type else asset.mime_type),
|
||||
file_size=asset.file_size,
|
||||
file_url=asset.storage_key,
|
||||
thumbnail_url=asset.thumbnail_url,
|
||||
@@ -35,7 +36,7 @@ class PostgresAssetRepository(AssetRepository):
|
||||
codec=asset.codec,
|
||||
status=asset.status.value,
|
||||
classification_status=asset.classification_status.value,
|
||||
classification_result=json.dumps(asset.metadata) if asset.metadata else None,
|
||||
classification_result=(json.dumps(asset.metadata) if asset.metadata else None),
|
||||
quality_score=asset.quality_score,
|
||||
uploaded_by_user_id=asset.uploaded_by_user_id or "system",
|
||||
created_at=asset.created_at,
|
||||
@@ -59,7 +60,12 @@ class PostgresAssetRepository(AssetRepository):
|
||||
) -> list[Asset]:
|
||||
result = await self.session.execute(
|
||||
select(AssetModel)
|
||||
.where(and_(AssetModel.project_id == project_id, AssetModel.workspace_id == workspace_id))
|
||||
.where(
|
||||
and_(
|
||||
AssetModel.project_id == project_id,
|
||||
AssetModel.workspace_id == workspace_id,
|
||||
)
|
||||
)
|
||||
.order_by(AssetModel.created_at.desc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
@@ -75,7 +81,12 @@ class PostgresAssetRepository(AssetRepository):
|
||||
) -> list[Asset]:
|
||||
result = await self.session.execute(
|
||||
select(AssetModel)
|
||||
.where(and_(AssetModel.asset_library_id == library_id, AssetModel.workspace_id == workspace_id))
|
||||
.where(
|
||||
and_(
|
||||
AssetModel.asset_library_id == library_id,
|
||||
AssetModel.workspace_id == workspace_id,
|
||||
)
|
||||
)
|
||||
.order_by(AssetModel.created_at.desc())
|
||||
.offset(skip)
|
||||
.limit(limit)
|
||||
@@ -118,7 +129,10 @@ class PostgresAssetRepository(AssetRepository):
|
||||
async def count_by_project(self, project_id: str, workspace_id: str) -> int:
|
||||
result = await self.session.execute(
|
||||
select(func.count(AssetModel.id)).where(
|
||||
and_(AssetModel.project_id == project_id, AssetModel.workspace_id == workspace_id)
|
||||
and_(
|
||||
AssetModel.project_id == project_id,
|
||||
AssetModel.workspace_id == workspace_id,
|
||||
)
|
||||
)
|
||||
)
|
||||
return result.scalar() or 0
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""
|
||||
数据库连接池管理
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import psycopg2
|
||||
from psycopg2 import pool
|
||||
from psycopg2.extras import RealDictCursor
|
||||
@@ -9,15 +11,15 @@ from psycopg2.extras import RealDictCursor
|
||||
|
||||
class DatabaseConnectionPool:
|
||||
"""PostgreSQL 连接池"""
|
||||
|
||||
_instance: Optional['DatabaseConnectionPool'] = None
|
||||
|
||||
_instance: Optional["DatabaseConnectionPool"] = None
|
||||
_pool: Optional[pool.ThreadedConnectionPool] = None
|
||||
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
|
||||
def initialize(
|
||||
self,
|
||||
connection_string: str,
|
||||
@@ -31,18 +33,18 @@ class DatabaseConnectionPool:
|
||||
maxconn=maxconn,
|
||||
dsn=connection_string,
|
||||
)
|
||||
|
||||
|
||||
def get_connection(self):
|
||||
"""从连接池获取连接"""
|
||||
if self._pool is None:
|
||||
raise RuntimeError("Connection pool not initialized")
|
||||
return self._pool.getconn()
|
||||
|
||||
|
||||
def put_connection(self, conn):
|
||||
"""将连接归还到连接池"""
|
||||
if self._pool is not None:
|
||||
self._pool.putconn(conn)
|
||||
|
||||
|
||||
def close_all(self):
|
||||
"""关闭所有连接"""
|
||||
if self._pool is not None:
|
||||
@@ -56,17 +58,17 @@ db_pool = DatabaseConnectionPool()
|
||||
|
||||
class PooledConnection:
|
||||
"""连接池上下文管理器"""
|
||||
|
||||
|
||||
def __init__(self, cursor_factory=RealDictCursor):
|
||||
self.cursor_factory = cursor_factory
|
||||
self.conn = None
|
||||
|
||||
|
||||
def __enter__(self):
|
||||
self.conn = db_pool.get_connection()
|
||||
if self.cursor_factory:
|
||||
self.conn.cursor_factory = self.cursor_factory
|
||||
return self.conn
|
||||
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self.conn:
|
||||
if exc_type is not None:
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""
|
||||
PostgreSQL Project Repository 实现
|
||||
"""
|
||||
from typing import Optional, List
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
|
||||
@@ -11,21 +13,23 @@ from packages.ports.project_repository import ProjectRepository
|
||||
|
||||
class PostgresProjectRepository(ProjectRepository):
|
||||
"""Project 仓储 PostgreSQL 实现"""
|
||||
|
||||
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
|
||||
|
||||
def _get_connection(self):
|
||||
"""获取数据库连接(使用连接池)"""
|
||||
from packages.adapters.postgres.connection_pool import PooledConnection
|
||||
|
||||
return PooledConnection()
|
||||
|
||||
|
||||
def save(self, project: Project) -> None:
|
||||
"""保存项目"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO projects (
|
||||
id, workspace_id, name, description, status,
|
||||
created_by, created_at, updated_at
|
||||
@@ -38,20 +42,22 @@ class PostgresProjectRepository(ProjectRepository):
|
||||
description = EXCLUDED.description,
|
||||
status = EXCLUDED.status,
|
||||
updated_at = EXCLUDED.updated_at
|
||||
""", {
|
||||
"id": project.id,
|
||||
"workspace_id": project.workspace_id,
|
||||
"name": project.name,
|
||||
"description": project.description,
|
||||
"status": project.status,
|
||||
"created_by": project.created_by,
|
||||
"created_at": project.created_at,
|
||||
"updated_at": project.updated_at,
|
||||
})
|
||||
""",
|
||||
{
|
||||
"id": project.id,
|
||||
"workspace_id": project.workspace_id,
|
||||
"name": project.name,
|
||||
"description": project.description,
|
||||
"status": project.status,
|
||||
"created_by": project.created_by,
|
||||
"created_at": project.created_at,
|
||||
"updated_at": project.updated_at,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_id(self, project_id: str) -> Optional[Project]:
|
||||
"""根据 ID 查找项目"""
|
||||
conn = self._get_connection()
|
||||
@@ -62,7 +68,7 @@ class PostgresProjectRepository(ProjectRepository):
|
||||
return self._row_to_project(row) if row else None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_workspace(self, workspace_id: str) -> List[Project]:
|
||||
"""根据 workspace 查找所有项目"""
|
||||
conn = self._get_connection()
|
||||
@@ -70,13 +76,13 @@ class PostgresProjectRepository(ProjectRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM projects WHERE workspace_id = %s ORDER BY created_at DESC",
|
||||
(workspace_id,)
|
||||
(workspace_id,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
return [self._row_to_project(row) for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_creator(self, user_id: str) -> List[Project]:
|
||||
"""根据创建者查找项目"""
|
||||
conn = self._get_connection()
|
||||
@@ -84,13 +90,13 @@ class PostgresProjectRepository(ProjectRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM projects WHERE created_by = %s ORDER BY created_at DESC",
|
||||
(user_id,)
|
||||
(user_id,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
return [self._row_to_project(row) for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def count_by_workspace(self, workspace_id: str) -> int:
|
||||
"""统计 workspace 的项目数量"""
|
||||
conn = self._get_connection()
|
||||
@@ -98,12 +104,12 @@ class PostgresProjectRepository(ProjectRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT COUNT(*) FROM projects WHERE workspace_id = %s",
|
||||
(workspace_id,)
|
||||
(workspace_id,),
|
||||
)
|
||||
return cur.fetchone()["count"]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete(self, project_id: str) -> bool:
|
||||
"""删除项目"""
|
||||
conn = self._get_connection()
|
||||
@@ -115,7 +121,7 @@ class PostgresProjectRepository(ProjectRepository):
|
||||
return deleted
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _row_to_project(self, row: dict) -> Project:
|
||||
"""将数据库行转换为 Project 对象"""
|
||||
return Project(
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
"""
|
||||
PostgreSQL User Repository 实现
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
from datetime import datetime
|
||||
|
||||
from packages.domain.entities import User
|
||||
from packages.ports.user_repository import UserRepository
|
||||
@@ -12,22 +14,24 @@ from packages.ports.user_repository import UserRepository
|
||||
|
||||
class PostgresUserRepository(UserRepository):
|
||||
"""User 仓储 PostgreSQL 实现"""
|
||||
|
||||
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
|
||||
|
||||
def _get_connection(self):
|
||||
"""获取数据库连接(使用连接池)"""
|
||||
from packages.adapters.postgres.connection_pool import PooledConnection
|
||||
|
||||
return PooledConnection()
|
||||
|
||||
|
||||
def save(self, user: User) -> None:
|
||||
"""保存用户"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
# Upsert (插入或更新)
|
||||
cur.execute("""
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO users (
|
||||
id, email, display_name, username, password_hash,
|
||||
email_verified, email_verification_token,
|
||||
@@ -50,24 +54,26 @@ class PostgresUserRepository(UserRepository):
|
||||
password_reset_expires_at = EXCLUDED.password_reset_expires_at,
|
||||
last_login_at = EXCLUDED.last_login_at,
|
||||
last_login_ip = EXCLUDED.last_login_ip
|
||||
""", {
|
||||
"id": user.id,
|
||||
"email": user.email,
|
||||
"display_name": user.display_name,
|
||||
"username": user.username,
|
||||
"password_hash": user.password_hash,
|
||||
"email_verified": user.email_verified,
|
||||
"email_verification_token": user.email_verification_token,
|
||||
"password_reset_token": user.password_reset_token,
|
||||
"password_reset_expires_at": user.password_reset_expires_at,
|
||||
"last_login_at": user.last_login_at,
|
||||
"last_login_ip": user.last_login_ip,
|
||||
"created_at": user.created_at,
|
||||
})
|
||||
""",
|
||||
{
|
||||
"id": user.id,
|
||||
"email": user.email,
|
||||
"display_name": user.display_name,
|
||||
"username": user.username,
|
||||
"password_hash": user.password_hash,
|
||||
"email_verified": user.email_verified,
|
||||
"email_verification_token": user.email_verification_token,
|
||||
"password_reset_token": user.password_reset_token,
|
||||
"password_reset_expires_at": user.password_reset_expires_at,
|
||||
"last_login_at": user.last_login_at,
|
||||
"last_login_ip": user.last_login_ip,
|
||||
"created_at": user.created_at,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_id(self, user_id: str) -> Optional[User]:
|
||||
"""根据 ID 查找用户"""
|
||||
conn = self._get_connection()
|
||||
@@ -75,13 +81,13 @@ class PostgresUserRepository(UserRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM users WHERE id = %s", (user_id,))
|
||||
row = cur.fetchone()
|
||||
|
||||
|
||||
if row:
|
||||
return self._row_to_user(row)
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_email(self, email: str) -> Optional[User]:
|
||||
"""根据邮箱查找用户"""
|
||||
conn = self._get_connection()
|
||||
@@ -89,13 +95,13 @@ class PostgresUserRepository(UserRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM users WHERE email = %s", (email.lower(),))
|
||||
row = cur.fetchone()
|
||||
|
||||
|
||||
if row:
|
||||
return self._row_to_user(row)
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_username(self, username: str) -> Optional[User]:
|
||||
"""根据用户名查找用户"""
|
||||
conn = self._get_connection()
|
||||
@@ -103,13 +109,13 @@ class PostgresUserRepository(UserRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM users WHERE username = %s", (username.lower(),))
|
||||
row = cur.fetchone()
|
||||
|
||||
|
||||
if row:
|
||||
return self._row_to_user(row)
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_verification_token(self, token: str) -> Optional[User]:
|
||||
"""根据邮箱验证令牌查找用户"""
|
||||
conn = self._get_connection()
|
||||
@@ -117,13 +123,13 @@ class PostgresUserRepository(UserRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM users WHERE email_verification_token = %s", (token,))
|
||||
row = cur.fetchone()
|
||||
|
||||
|
||||
if row:
|
||||
return self._row_to_user(row)
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_password_reset_token(self, token: str) -> Optional[User]:
|
||||
"""根据密码重置令牌查找用户"""
|
||||
conn = self._get_connection()
|
||||
@@ -131,13 +137,13 @@ class PostgresUserRepository(UserRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM users WHERE password_reset_token = %s", (token,))
|
||||
row = cur.fetchone()
|
||||
|
||||
|
||||
if row:
|
||||
return self._row_to_user(row)
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete(self, user_id: str) -> bool:
|
||||
"""删除用户"""
|
||||
conn = self._get_connection()
|
||||
@@ -149,7 +155,7 @@ class PostgresUserRepository(UserRepository):
|
||||
return deleted
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _row_to_user(self, row: dict) -> User:
|
||||
"""将数据库行转换为 User 对象"""
|
||||
return User(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""
|
||||
PostgreSQL WorkspaceInvitation Repository 实现
|
||||
"""
|
||||
from typing import Optional, List
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
|
||||
@@ -11,21 +13,23 @@ from packages.ports.workspace_invitation_repository import WorkspaceInvitationRe
|
||||
|
||||
class PostgresWorkspaceInvitationRepository(WorkspaceInvitationRepository):
|
||||
"""WorkspaceInvitation 仓储 PostgreSQL 实现"""
|
||||
|
||||
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
|
||||
|
||||
def _get_connection(self):
|
||||
"""获取数据库连接(使用连接池)"""
|
||||
from packages.adapters.postgres.connection_pool import PooledConnection
|
||||
|
||||
return PooledConnection()
|
||||
|
||||
|
||||
def save(self, invitation: WorkspaceInvitation) -> None:
|
||||
"""保存邀请"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO workspace_invitations (
|
||||
id, workspace_id, email, role, token,
|
||||
invited_by, expires_at, status, created_at
|
||||
@@ -35,32 +39,37 @@ class PostgresWorkspaceInvitationRepository(WorkspaceInvitationRepository):
|
||||
)
|
||||
ON CONFLICT (id) DO UPDATE SET
|
||||
status = EXCLUDED.status
|
||||
""", {
|
||||
"id": invitation.id,
|
||||
"workspace_id": invitation.workspace_id,
|
||||
"email": invitation.email,
|
||||
"role": invitation.role,
|
||||
"token": invitation.token,
|
||||
"invited_by": invitation.invited_by,
|
||||
"expires_at": invitation.expires_at,
|
||||
"status": invitation.status,
|
||||
"created_at": invitation.created_at,
|
||||
})
|
||||
""",
|
||||
{
|
||||
"id": invitation.id,
|
||||
"workspace_id": invitation.workspace_id,
|
||||
"email": invitation.email,
|
||||
"role": invitation.role,
|
||||
"token": invitation.token,
|
||||
"invited_by": invitation.invited_by,
|
||||
"expires_at": invitation.expires_at,
|
||||
"status": invitation.status,
|
||||
"created_at": invitation.created_at,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_id(self, invitation_id: str) -> Optional[WorkspaceInvitation]:
|
||||
"""根据 ID 查找邀请"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM workspace_invitations WHERE id = %s", (invitation_id,))
|
||||
cur.execute(
|
||||
"SELECT * FROM workspace_invitations WHERE id = %s",
|
||||
(invitation_id,),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return self._row_to_invitation(row) if row else None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_token(self, token: str) -> Optional[WorkspaceInvitation]:
|
||||
"""根据 token 查找邀请"""
|
||||
conn = self._get_connection()
|
||||
@@ -71,7 +80,7 @@ class PostgresWorkspaceInvitationRepository(WorkspaceInvitationRepository):
|
||||
return self._row_to_invitation(row) if row else None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_email(self, email: str) -> List[WorkspaceInvitation]:
|
||||
"""根据邮箱查找所有邀请"""
|
||||
conn = self._get_connection()
|
||||
@@ -79,28 +88,31 @@ class PostgresWorkspaceInvitationRepository(WorkspaceInvitationRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM workspace_invitations WHERE email = %s ORDER BY created_at DESC",
|
||||
(email,)
|
||||
(email,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
return [self._row_to_invitation(row) for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_pending_by_email(self, email: str) -> List[WorkspaceInvitation]:
|
||||
"""查找邮箱的待处理邀请"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
cur.execute(
|
||||
"""
|
||||
SELECT * FROM workspace_invitations
|
||||
WHERE email = %s AND status = 'pending' AND expires_at > NOW()
|
||||
ORDER BY created_at DESC
|
||||
""", (email,))
|
||||
""",
|
||||
(email,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
return [self._row_to_invitation(row) for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete(self, invitation_id: str) -> bool:
|
||||
"""删除邀请"""
|
||||
conn = self._get_connection()
|
||||
@@ -112,7 +124,7 @@ class PostgresWorkspaceInvitationRepository(WorkspaceInvitationRepository):
|
||||
return deleted
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _row_to_invitation(self, row: dict) -> WorkspaceInvitation:
|
||||
"""将数据库行转换为 WorkspaceInvitation 对象"""
|
||||
return WorkspaceInvitation(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""
|
||||
PostgreSQL WorkspaceMember Repository 实现
|
||||
"""
|
||||
from typing import Optional, List
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
|
||||
@@ -11,21 +13,23 @@ from packages.ports.workspace_member_repository import WorkspaceMemberRepository
|
||||
|
||||
class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
"""WorkspaceMember 仓储 PostgreSQL 实现"""
|
||||
|
||||
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
|
||||
|
||||
def _get_connection(self):
|
||||
"""获取数据库连接(使用连接池)"""
|
||||
from packages.adapters.postgres.connection_pool import PooledConnection
|
||||
|
||||
return PooledConnection()
|
||||
|
||||
|
||||
def save(self, member: WorkspaceMember) -> None:
|
||||
"""保存成员"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO workspace_members (
|
||||
id, workspace_id, user_id, role, invited_by, joined_at
|
||||
) VALUES (
|
||||
@@ -34,18 +38,20 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
)
|
||||
ON CONFLICT (workspace_id, user_id) DO UPDATE SET
|
||||
role = EXCLUDED.role
|
||||
""", {
|
||||
"id": member.id,
|
||||
"workspace_id": member.workspace_id,
|
||||
"user_id": member.user_id,
|
||||
"role": member.role,
|
||||
"invited_by": member.invited_by,
|
||||
"joined_at": member.joined_at,
|
||||
})
|
||||
""",
|
||||
{
|
||||
"id": member.id,
|
||||
"workspace_id": member.workspace_id,
|
||||
"user_id": member.user_id,
|
||||
"role": member.role,
|
||||
"invited_by": member.invited_by,
|
||||
"joined_at": member.joined_at,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_id(self, member_id: str) -> Optional[WorkspaceMember]:
|
||||
"""根据 ID 查找成员"""
|
||||
conn = self._get_connection()
|
||||
@@ -56,7 +62,7 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
return self._row_to_member(row) if row else None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_workspace_and_user(
|
||||
self,
|
||||
workspace_id: str,
|
||||
@@ -68,13 +74,13 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM workspace_members WHERE workspace_id = %s AND user_id = %s",
|
||||
(workspace_id, user_id)
|
||||
(workspace_id, user_id),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
return self._row_to_member(row) if row else None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_user(self, user_id: str) -> List[WorkspaceMember]:
|
||||
"""查找用户的所有成员记录"""
|
||||
conn = self._get_connection()
|
||||
@@ -82,13 +88,13 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM workspace_members WHERE user_id = %s ORDER BY joined_at DESC",
|
||||
(user_id,)
|
||||
(user_id,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
return [self._row_to_member(row) for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_workspace(self, workspace_id: str) -> List[WorkspaceMember]:
|
||||
"""查找 workspace 的所有成员"""
|
||||
conn = self._get_connection()
|
||||
@@ -96,13 +102,13 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT * FROM workspace_members WHERE workspace_id = %s ORDER BY joined_at",
|
||||
(workspace_id,)
|
||||
(workspace_id,),
|
||||
)
|
||||
rows = cur.fetchall()
|
||||
return [self._row_to_member(row) for row in rows]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def count_by_workspace(self, workspace_id: str) -> int:
|
||||
"""统计 workspace 的成员数量"""
|
||||
conn = self._get_connection()
|
||||
@@ -110,12 +116,12 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute(
|
||||
"SELECT COUNT(*) FROM workspace_members WHERE workspace_id = %s",
|
||||
(workspace_id,)
|
||||
(workspace_id,),
|
||||
)
|
||||
return cur.fetchone()["count"]
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete(self, member_id: str) -> bool:
|
||||
"""删除成员"""
|
||||
conn = self._get_connection()
|
||||
@@ -127,7 +133,7 @@ class PostgresWorkspaceMemberRepository(WorkspaceMemberRepository):
|
||||
return deleted
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _row_to_member(self, row: dict) -> WorkspaceMember:
|
||||
"""将数据库行转换为 WorkspaceMember 对象"""
|
||||
return WorkspaceMember(
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
"""
|
||||
PostgreSQL Workspace Repository 实现
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
|
||||
@@ -11,21 +13,23 @@ from packages.ports.workspace_repository import WorkspaceRepository
|
||||
|
||||
class PostgresWorkspaceRepository(WorkspaceRepository):
|
||||
"""Workspace 仓储 PostgreSQL 实现"""
|
||||
|
||||
|
||||
def __init__(self, connection_string: str):
|
||||
self.connection_string = connection_string
|
||||
|
||||
|
||||
def _get_connection(self):
|
||||
"""获取数据库连接(使用连接池)"""
|
||||
from packages.adapters.postgres.connection_pool import PooledConnection
|
||||
|
||||
return PooledConnection()
|
||||
|
||||
|
||||
def save(self, workspace: Workspace) -> None:
|
||||
"""保存工作空间"""
|
||||
conn = self._get_connection()
|
||||
try:
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("""
|
||||
cur.execute(
|
||||
"""
|
||||
INSERT INTO workspaces (
|
||||
id, name, owner_user_id, subscription_plan,
|
||||
subscription_status, subscription_expires_at,
|
||||
@@ -43,22 +47,24 @@ class PostgresWorkspaceRepository(WorkspaceRepository):
|
||||
max_projects = EXCLUDED.max_projects,
|
||||
max_storage_gb = EXCLUDED.max_storage_gb,
|
||||
used_storage_gb = EXCLUDED.used_storage_gb
|
||||
""", {
|
||||
"id": workspace.id,
|
||||
"name": workspace.name,
|
||||
"owner_user_id": workspace.owner_user_id,
|
||||
"subscription_plan": workspace.subscription_plan,
|
||||
"subscription_status": workspace.subscription_status,
|
||||
"subscription_expires_at": workspace.subscription_expires_at,
|
||||
"max_projects": workspace.max_projects,
|
||||
"max_storage_gb": workspace.max_storage_gb,
|
||||
"used_storage_gb": workspace.used_storage_gb,
|
||||
"created_at": workspace.created_at,
|
||||
})
|
||||
""",
|
||||
{
|
||||
"id": workspace.id,
|
||||
"name": workspace.name,
|
||||
"owner_user_id": workspace.owner_user_id,
|
||||
"subscription_plan": workspace.subscription_plan,
|
||||
"subscription_status": workspace.subscription_status,
|
||||
"subscription_expires_at": workspace.subscription_expires_at,
|
||||
"max_projects": workspace.max_projects,
|
||||
"max_storage_gb": workspace.max_storage_gb,
|
||||
"used_storage_gb": workspace.used_storage_gb,
|
||||
"created_at": workspace.created_at,
|
||||
},
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def find_by_id(self, workspace_id: str) -> Optional[Workspace]:
|
||||
"""根据 ID 查找工作空间"""
|
||||
conn = self._get_connection()
|
||||
@@ -66,13 +72,13 @@ class PostgresWorkspaceRepository(WorkspaceRepository):
|
||||
with conn.cursor() as cur:
|
||||
cur.execute("SELECT * FROM workspaces WHERE id = %s", (workspace_id,))
|
||||
row = cur.fetchone()
|
||||
|
||||
|
||||
if row:
|
||||
return self._row_to_workspace(row)
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def delete(self, workspace_id: str) -> bool:
|
||||
"""删除工作空间"""
|
||||
conn = self._get_connection()
|
||||
@@ -84,7 +90,7 @@ class PostgresWorkspaceRepository(WorkspaceRepository):
|
||||
return deleted
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _row_to_workspace(self, row: dict) -> Workspace:
|
||||
"""将数据库行转换为 Workspace 对象"""
|
||||
return Workspace(
|
||||
|
||||
Reference in New Issue
Block a user