celery worker poll 频次机制调整| celery beat 设置poll检测任务| 拆镜复刻切片删除API | 交易流水时区BUG

This commit is contained in:
2026-07-02 09:36:38 +08:00
parent c6df04a895
commit f6c5032c7b
22 changed files with 1039 additions and 124 deletions
@@ -12,6 +12,7 @@ from sqlalchemy.orm.attributes import flag_modified
from app.config import settings
from app.enums.common import ModuleEventTypeEnum, ModuleProjectStatusEnum, ModulePromptTypeEnum, ModuleStepStatusEnum
from app.enums.generation_task import ChatGenerationPipelineStage, ChatGenerationTaskStatus
from app.enums.shot_replicate import ShotReplicateGenerationModeEnum, ShotReplicateStepCodeEnum, ModuleCodeEnum
from app.models.chat_generation_task import ChatGenerationTask
from app.models.module_generation_project import ModuleGenerationProject
@@ -335,6 +336,8 @@ async def _soft_delete_steps_from_index(
project: ModuleGenerationProject,
start_index: int,
deleted_at: datetime | None = None,
refund_unfinished: bool = True,
release_stats: dict[str, int] | None = None,
) -> None:
await _base_soft_delete_steps_from_index(
db,
@@ -343,6 +346,8 @@ async def _soft_delete_steps_from_index(
config=FLOW_CONFIG,
log_module_event=log_module_event,
deleted_at=deleted_at,
refund_unfinished=refund_unfinished,
release_stats=release_stats,
)
@@ -1406,6 +1411,60 @@ async def handle_chat_generation_task_failed(db: AsyncSession, task: ChatGenerat
await log_module_event(db, project=project, step=step, event_type=ModuleEventTypeEnum.CHAT_TASK_FAILED.value, message=project.error_message, detail={"chat_task_id": task.id})
_ACTIVE_DELETE_BLOCK_STATUSES = {
ChatGenerationTaskStatus.PENDING.value,
ChatGenerationTaskStatus.GENERATING.value,
}
_ACTIVE_DELETE_BLOCK_STAGES = {
ChatGenerationPipelineStage.QUEUED.value,
ChatGenerationPipelineStage.PREPARING.value,
ChatGenerationPipelineStage.CREATING_PROVIDER_TASK.value,
ChatGenerationPipelineStage.WAITING_REMOTE.value,
ChatGenerationPipelineStage.POLLING.value,
ChatGenerationPipelineStage.RESULT_READY.value,
ChatGenerationPipelineStage.DOWNLOAD_QUEUED.value,
ChatGenerationPipelineStage.DOWNLOADING.value,
ChatGenerationPipelineStage.RETRY_WAITING.value,
}
async def _assert_project_has_no_active_chat_tasks_for_delete(
db: AsyncSession,
*,
project: ModuleGenerationProject,
) -> None:
"""用户主动删除项目/切片时不退款;如仍有异步生成任务进行中,直接拦截。"""
step_result = await db.execute(
select(ModuleGenerationStep.chat_task_id)
.where(
ModuleGenerationStep.project_id == project.id,
ModuleGenerationStep.module == MODULE,
ModuleGenerationStep.deleted_at.is_(None),
ModuleGenerationStep.chat_task_id.is_not(None),
)
)
chat_task_ids = [task_id for task_id in step_result.scalars().all() if task_id]
if not chat_task_ids:
return
task_result = await db.execute(
select(ChatGenerationTask)
.where(
ChatGenerationTask.id.in_(chat_task_ids),
ChatGenerationTask.deleted_at.is_(None),
)
.with_for_update()
)
active_tasks = []
for task in task_result.scalars().all():
if task.status in _ACTIVE_DELETE_BLOCK_STATUSES or (task.pipeline_stage in _ACTIVE_DELETE_BLOCK_STAGES):
active_tasks.append(task.id)
if active_tasks:
raise HTTPException(status_code=400, detail="当前拆镜复刻项目仍有生成中任务,暂不能删除")
async def mark_shot_replicate_step_dispatch_failed(
db: AsyncSession,
*,
@@ -1462,13 +1521,45 @@ async def mark_shot_replicate_step_dispatch_failed(
)
async def delete_shot_replicate_project(db: AsyncSession, *, current_user: User, project_id: str) -> ShotReplicateDeleteOut:
async def delete_shot_replicate_project(
db: AsyncSession,
*,
current_user: User,
project_id: str,
refund_unfinished: bool = False,
) -> ShotReplicateDeleteOut:
project = await _get_project_for_user(db, project_id=project_id, user=current_user, for_update=True)
deleted_at = _now()
release_stats: dict[str, int] = {"released_size_bytes": 0}
if not refund_unfinished:
await _assert_project_has_no_active_chat_tasks_for_delete(db, project=project)
project.deleted_at = deleted_at
await _soft_delete_steps_from_index(db, project=project, start_index=1, deleted_at=deleted_at)
await log_module_event(db, project=project, event_type=ModuleEventTypeEnum.PROJECT_DELETED.value, message="软删除拆镜复刻项目")
return ShotReplicateDeleteOut(message="项目已删除", project_id=project.id, deleted=True)
await _soft_delete_steps_from_index(
db,
project=project,
start_index=1,
deleted_at=deleted_at,
refund_unfinished=refund_unfinished,
release_stats=release_stats,
)
await log_module_event(
db,
project=project,
event_type=ModuleEventTypeEnum.PROJECT_DELETED.value,
message="软删除拆镜复刻项目",
detail={
"refund_unfinished": refund_unfinished,
"released_size_bytes": int(release_stats.get("released_size_bytes", 0)),
},
)
return ShotReplicateDeleteOut(
message="项目已删除",
project_id=project.id,
deleted=True,
released_size_bytes=int(release_stats.get("released_size_bytes", 0)),
)
async def create_shot_replicate_project_from_segment(