mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
refactor: close transactional boundary debt batch
This commit is contained in:
+9
-2
@@ -10,7 +10,7 @@ from app.application.configuration import (
|
||||
get_api_runtime_config_snapshot,
|
||||
)
|
||||
from app.application.messaging.chat import AsyncAgentChatRepository, AsyncUnitOfWork
|
||||
from app.application.outbox import AsyncOutboxTransaction
|
||||
from app.application.outbox import AsyncOutboxDispatchStore, AsyncOutboxStager
|
||||
from app.application.subscription.delete import SubscribeDeletionRepository
|
||||
from app.application.subscription.identity import SubscribeIdentityDeletionRepository
|
||||
from app.application.subscription.mutation import (
|
||||
@@ -161,6 +161,13 @@ def get_subscription_transaction(
|
||||
def get_subscription_outbox(
|
||||
session: object = Depends(get_subscription_session),
|
||||
runtime: SubscriptionRuntime = Depends(get_subscription_runtime),
|
||||
) -> AsyncOutboxTransaction:
|
||||
) -> AsyncOutboxStager:
|
||||
"""构造与订阅写入共享请求会话的 outbox 端口。"""
|
||||
return runtime.outbox(session)
|
||||
|
||||
|
||||
def get_subscription_outbox_store(
|
||||
runtime: SubscriptionRuntime = Depends(get_subscription_runtime),
|
||||
) -> AsyncOutboxDispatchStore:
|
||||
"""返回使用独立短事务的订阅 outbox 派发存储。"""
|
||||
return runtime.dispatch_store
|
||||
|
||||
@@ -20,6 +20,7 @@ from app.application.security.user import (
|
||||
UserRepository,
|
||||
UserService,
|
||||
)
|
||||
from app.application.security.userconfig import get_configured_user_configuration
|
||||
from app.schemas.token import TokenPayload as _SchemaTokenPayload
|
||||
from app.startup.composition.context import HostRuntime
|
||||
|
||||
@@ -36,6 +37,7 @@ def get_user_service(
|
||||
unit_of_work=cast(
|
||||
AsyncUnitOfWork, runtime.persistence.async_transaction(db)
|
||||
),
|
||||
configuration=get_configured_user_configuration(),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -13,12 +13,13 @@ from app.api.context import (
|
||||
get_host_runtime,
|
||||
get_subscription_history_repository,
|
||||
get_subscription_outbox,
|
||||
get_subscription_outbox_store,
|
||||
get_subscription_repository,
|
||||
get_subscription_transaction,
|
||||
get_sync_session,
|
||||
resolve_background_task_registry,
|
||||
)
|
||||
from app.application.outbox import AsyncOutboxTransaction
|
||||
from app.application.outbox import AsyncOutboxDispatchStore, AsyncOutboxStager
|
||||
from app.application.scheduling import start_scheduler_job
|
||||
from app.application.servarr import ServarrSubscriptionService
|
||||
from app.application.subscription.delete import (
|
||||
@@ -64,7 +65,10 @@ async def _publish_subscribe_modified(payload: dict[str, Any]) -> None:
|
||||
def get_delete_subscribe_command(
|
||||
repository_port: object = Depends(get_subscription_repository),
|
||||
unit_of_work: object = Depends(get_subscription_transaction),
|
||||
outbox: AsyncOutboxTransaction = Depends(get_subscription_outbox),
|
||||
outbox: AsyncOutboxStager = Depends(get_subscription_outbox),
|
||||
dispatch_store: AsyncOutboxDispatchStore = Depends(
|
||||
get_subscription_outbox_store
|
||||
),
|
||||
) -> DeleteSubscribeCommand:
|
||||
"""组装请求级订阅删除用例及其具体适配器。"""
|
||||
return DeleteSubscribeCommand(
|
||||
@@ -73,6 +77,7 @@ def get_delete_subscribe_command(
|
||||
publish_deleted=_publish_subscribe_deleted,
|
||||
report_deleted=MoviePilotServerHelper.async_sub_done_durable,
|
||||
outbox=outbox,
|
||||
dispatch_store=dispatch_store,
|
||||
)
|
||||
|
||||
|
||||
@@ -90,7 +95,10 @@ def _log_subscribe_deleted_event_error(
|
||||
def get_delete_subscriptions_by_identity_command(
|
||||
repository_port: object = Depends(get_subscription_repository),
|
||||
unit_of_work: object = Depends(get_subscription_transaction),
|
||||
outbox: AsyncOutboxTransaction = Depends(get_subscription_outbox),
|
||||
outbox: AsyncOutboxStager = Depends(get_subscription_outbox),
|
||||
dispatch_store: AsyncOutboxDispatchStore = Depends(
|
||||
get_subscription_outbox_store
|
||||
),
|
||||
) -> DeleteSubscriptionsByIdentityCommand:
|
||||
"""组装请求级按媒体身份删除订阅用例。"""
|
||||
return DeleteSubscriptionsByIdentityCommand(
|
||||
@@ -99,6 +107,7 @@ def get_delete_subscriptions_by_identity_command(
|
||||
publish_deleted=_publish_subscribe_deleted,
|
||||
handle_event_error=_log_subscribe_deleted_event_error,
|
||||
outbox=outbox,
|
||||
dispatch_store=dispatch_store,
|
||||
)
|
||||
|
||||
|
||||
@@ -165,7 +174,10 @@ def get_subscription_mutation_service(
|
||||
get_subscription_history_repository
|
||||
),
|
||||
unit_of_work: object = Depends(get_subscription_transaction),
|
||||
outbox: AsyncOutboxTransaction = Depends(get_subscription_outbox),
|
||||
outbox: AsyncOutboxStager = Depends(get_subscription_outbox),
|
||||
dispatch_store: AsyncOutboxDispatchStore = Depends(
|
||||
get_subscription_outbox_store
|
||||
),
|
||||
) -> SubscriptionMutationService:
|
||||
"""组装异步订阅写服务。"""
|
||||
return SubscriptionMutationService(
|
||||
@@ -173,6 +185,7 @@ def get_subscription_mutation_service(
|
||||
history_repository=history_repository,
|
||||
unit_of_work=cast(MutationUnitOfWork, unit_of_work),
|
||||
outbox=outbox,
|
||||
dispatch_store=dispatch_store,
|
||||
publish_modified=_publish_subscribe_modified,
|
||||
)
|
||||
|
||||
|
||||
@@ -101,7 +101,11 @@ def _verify_passkey_and_update(
|
||||
)
|
||||
|
||||
if success:
|
||||
service.update_last_used(passkey, new_sign_count)
|
||||
success = service.compare_and_update_sign_count(
|
||||
passkey_id=passkey.id,
|
||||
expected_sign_count=int(passkey.sign_count or 0),
|
||||
sign_count=new_sign_count,
|
||||
)
|
||||
|
||||
return success, new_sign_count
|
||||
|
||||
@@ -142,16 +146,11 @@ async def mfa_status(
|
||||
service: UserService = Depends(get_user_service),
|
||||
) -> Any:
|
||||
"""
|
||||
检查指定用户是否启用了二次验证
|
||||
检查指定启用用户是否开启 OTP,并隐藏账号不存在或禁用状态。
|
||||
"""
|
||||
user = await service.get_by_name(username)
|
||||
if not user:
|
||||
return _SchemaResponse(success=False, message="用户不存在")
|
||||
|
||||
# 检查是否启用了OTP
|
||||
has_otp = user.is_otp
|
||||
|
||||
return _SchemaResponse(success=True, data={"enabled": bool(has_otp)})
|
||||
has_otp = bool(user and user.is_active and user.is_otp)
|
||||
return _SchemaResponse(success=True, data={"enabled": has_otp})
|
||||
|
||||
|
||||
# ==================== OTP 相关接口 ====================
|
||||
|
||||
+23
-13
@@ -11,7 +11,11 @@ from app.api.dependencies.auth import (
|
||||
)
|
||||
from app.api.response import ResponseAPIRouter
|
||||
from app.application.security.token import PasswordTooLongError, get_password_hash
|
||||
from app.application.security.user import UserService
|
||||
from app.application.security.user import (
|
||||
LastActiveSuperuserError,
|
||||
UserNameConflictError,
|
||||
UserService,
|
||||
)
|
||||
from app.application.security.userconfig import get_configured_user_configuration
|
||||
from app.schemas.common import FileNameData as _SchemaFileNameData
|
||||
from app.schemas.common import ValueData as _SchemaValueData
|
||||
@@ -44,9 +48,6 @@ async def create_user(
|
||||
"""
|
||||
新增用户
|
||||
"""
|
||||
user = await service.get_by_name(user_in.name)
|
||||
if user:
|
||||
return _SchemaResponse(success=False, message="用户已存在")
|
||||
user_info = user_in.model_dump()
|
||||
if user_info.get("password"):
|
||||
try:
|
||||
@@ -54,7 +55,10 @@ async def create_user(
|
||||
except PasswordTooLongError as error:
|
||||
return _SchemaResponse(success=False, message=str(error))
|
||||
user_info.pop("password")
|
||||
user = await service.create(user_info)
|
||||
try:
|
||||
user = await service.create(user_info)
|
||||
except UserNameConflictError:
|
||||
return _SchemaResponse(success=False, message="用户已存在")
|
||||
return _SchemaResponse(success=True if user else False)
|
||||
|
||||
|
||||
@@ -86,14 +90,14 @@ async def update_user(
|
||||
user_name = user_info.get("name")
|
||||
if not user_name:
|
||||
return _SchemaResponse(success=False, message="用户名不能为空")
|
||||
# 新用户名去重
|
||||
users = await service.list()
|
||||
for u in users:
|
||||
if u.name == user_name and u.id != user_info["id"]:
|
||||
return _SchemaResponse(success=False, message="用户名已被使用")
|
||||
if not user:
|
||||
return _SchemaResponse(success=False, message="用户不存在")
|
||||
await service.update(user_info["id"], user_info)
|
||||
try:
|
||||
await service.update(user_info["id"], user_info)
|
||||
except UserNameConflictError:
|
||||
return _SchemaResponse(success=False, message="用户名已被使用")
|
||||
except LastActiveSuperuserError:
|
||||
return _SchemaResponse(success=False, message="必须保留至少一个启用的超级管理员")
|
||||
return _SchemaResponse(success=True)
|
||||
|
||||
|
||||
@@ -180,7 +184,10 @@ async def delete_user_by_id(
|
||||
user = await service.get_by_id(user_id)
|
||||
if not user:
|
||||
return _SchemaResponse(success=False, message="用户不存在")
|
||||
await service.delete(user_id)
|
||||
try:
|
||||
await service.delete(user_id)
|
||||
except LastActiveSuperuserError:
|
||||
return _SchemaResponse(success=False, message="必须保留至少一个启用的超级管理员")
|
||||
return _SchemaResponse(success=True)
|
||||
|
||||
|
||||
@@ -197,7 +204,10 @@ async def delete_user_by_name(
|
||||
user = await service.get_by_name(user_name)
|
||||
if not user:
|
||||
return _SchemaResponse(success=False, message="用户不存在")
|
||||
await service.delete(user.id)
|
||||
try:
|
||||
await service.delete(user.id)
|
||||
except LastActiveSuperuserError:
|
||||
return _SchemaResponse(success=False, message="必须保留至少一个启用的超级管理员")
|
||||
return _SchemaResponse(success=True)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user