Files
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

178 lines
6.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
整理任务:整理链的进程内工作项。
TransferTask 此前住在 app/schemas/transfer.py,但它不是出网的 DTO——meta 装的是领域侧
的 MetaBase 子类,mediainfo 装的是领域侧的 MediaInfo / MusicInfo,都带行为而非纯数据。
放在 app.schemas 的代价是它没法命名自己真正装的类型:app.schemas 一旦 import 领域类型,
app.schemas -> app.schemas.transfer -> app.domain.* -> app.schemas.types -> app.schemas
就闭环,仓库自己的 test_migrated_modules_are_not_in_import_cycles 会红(已实测)。于是
两个字段只能标成 Optional[Any],把「这里到底能放什么」这件事整个交给了口头约定。
搬到应用层就没有这个约束:app.application 允许依赖 app.domain 与 app.schemas,两个
字段因此能标出真实类型。它面向前端的投影仍是 app/schemas/transfer.py 里的
TransferJob / TransferJobTask,那两个用 app.schemas 的同名 DTO——一个是工作项,一个是
视图,分开表达之后两边都不必再迁就对方。
"""
from typing import (
Any,
Callable,
List,
Optional,
Tuple,
)
from app.application.transfer.feedback import TransferFailureNotification
from app.application.transfer.jobs import (
DirectorySize,
JobManager,
configure_directory_size,
job_lock,
)
from app.application.transfer.models import (
TRANSFER_ADMISSION_ACCEPTED,
TRANSFER_ADMISSION_PLANNED,
TRANSFER_ADMISSION_PROVIDER_PENDING,
TRANSFER_PLAN_CHECKPOINT_LEGACY_VERSION,
TRANSFER_PLAN_CHECKPOINT_VERSION,
TRANSFER_PLANNING_INPUT_VERSION,
TRANSFER_PROVIDER_INVOCATION_VERSION,
TransferAdmission,
TransferAdmissionConflictError,
TransferAdmissionProjectionError,
TransferAdmissionRepository,
TransferCallback,
TransferLeaseLostError,
TransferPlanCheckpoint,
TransferPlanItem,
TransferPlanningInput,
TransferPlanningStateError,
TransferProviderInvocationSnapshot,
TransferProviderReference,
TransferQueue,
TransferTask,
)
from app.application.transfer.notifications import (
TransferFailureNotificationAggregator,
build_transfer_failure_group_key,
)
from app.schemas.file import FileItem
from app.schemas.transfer import TransferJob
__all__ = [
"DirectorySize",
"JobManager",
"configure_directory_size",
"job_lock",
"TRANSFER_ADMISSION_ACCEPTED",
"TRANSFER_ADMISSION_PLANNED",
"TRANSFER_ADMISSION_PROVIDER_PENDING",
"TRANSFER_PLAN_CHECKPOINT_LEGACY_VERSION",
"TRANSFER_PLAN_CHECKPOINT_VERSION",
"TRANSFER_PLANNING_INPUT_VERSION",
"TRANSFER_PROVIDER_INVOCATION_VERSION",
"TransferAdmission",
"TransferAdmissionConflictError",
"TransferAdmissionProjectionError",
"TransferAdmissionRepository",
"TransferCallback",
"TransferFailureNotification",
"TransferFailureNotificationAggregator",
"TransferLeaseLostError",
"TransferPlanCheckpoint",
"TransferPlanItem",
"TransferPlanningInput",
"TransferPlanningStateError",
"TransferProviderInvocationSnapshot",
"TransferProviderReference",
"TransferQueue",
"TransferTask",
"build_transfer_failure_group_key",
"TransferQueueService",
]
class TransferQueueService:
"""
协调整理任务登记、入队、移除和队列视图查询。
"""
def __init__(
self,
*,
register_task: Callable[[TransferTask], bool],
admit_task: Callable[[TransferTask], TransferAdmission],
enqueue: Callable[[TransferQueue], None],
before_enqueue: Callable[[TransferTask], None],
enqueue_failed: Callable[[TransferTask, Exception], None],
remove_task: Callable[[FileItem], None],
list_tasks: Callable[[], List[TransferJob]],
expire_tasks: Callable[[], None],
list_tasks_page: Optional[
Callable[[int, int], Tuple[List[Any], int]]
] = None,
) -> None:
"""
保存队列用例依赖,避免 Application 服务绑定具体线程队列实现。
"""
self._register_task = register_task
self._admit_task = admit_task
self._enqueue = enqueue
self._before_enqueue = before_enqueue
self._enqueue_failed = enqueue_failed
self._remove_task = remove_task
self._list_tasks = list_tasks
self._expire_tasks = expire_tasks
self._list_tasks_page = list_tasks_page
def put(self, task: TransferTask, callback: TransferCallback) -> bool:
"""
先持久化准入事实再入队;任何前置失败都撤销内存作业视图。
"""
if not task or not self._register_task(task):
return False
try:
admission = self._admit_task(task)
task.bind_admission_task_id(admission.task_id)
except Exception:
self._remove_task(task.fileitem)
raise
try:
self._before_enqueue(task)
self._enqueue(TransferQueue(task=task, callback=callback))
except Exception as err:
try:
self._enqueue_failed(task, err)
finally:
self._remove_task(task.fileitem)
raise
return True
def remove(self, fileitem: FileItem) -> None:
"""
从整理任务视图移除指定文件。
"""
if fileitem:
self._remove_task(fileitem)
def list(self) -> List[TransferJob]:
"""
先处理失活任务,再返回当前整理作业视图。
"""
self._expire_tasks()
return self._list_tasks()
def list_page(self, page: int, count: int) -> Tuple[List[Any], int]:
"""
返回受限队列快照,避免前端请求触发全量 DTO 序列化。
"""
if page < 1 or count < 1:
raise ValueError("队列分页参数必须为正数")
self._expire_tasks()
if self._list_tasks_page is not None:
return self._list_tasks_page(page, count)
tasks = self._list_tasks()
start = (page - 1) * count
return tasks[start:start + count], len(tasks)