功能:增加旧测试数据闭包纳入

This commit is contained in:
焦龙言
2026-09-23 13:15:59 +08:00
parent c97f6ed3bd
commit 91cf862248
5 changed files with 1009 additions and 3 deletions
@@ -0,0 +1,167 @@
import hashlib
import hmac
from datetime import datetime, timedelta
from typing import Any
from sqlalchemy.ext.asyncio import AsyncSession
from common.vo import CrudResponseModel
from exceptions.exception import PermissionException, ServiceWarning
from module_admin.entity.vo.user_vo import CurrentUserModel
from module_business.dao.test_batch_adoption_dao import TestBatchAdoptionDao
from module_business.dao.test_batch_dao import TestBatchDao
from module_business.domain.enums import TestBatchStatus
from module_business.domain.test_batch_graph import (
BATCH_LABELS,
BATCH_MODELS,
TestBatchGraph,
issue_preview_token,
selection_hash,
)
from module_business.entity.vo.test_batch_vo import (
AdoptionExecuteRequestModel,
AdoptionPreviewRequestModel,
BatchImpactModel,
TestBatchPreviewModel,
)
from module_business.service.test_batch_access_service import TestBatchAccessService
class TestBatchAdoptionService:
"""预览并原子纳入已存在的正式数据闭包。"""
@staticmethod
def _assert_admin(current_user: CurrentUserModel) -> None:
TestBatchAccessService.assert_super_admin(current_user)
@staticmethod
def _user_name(current_user: CurrentUserModel) -> str:
user = current_user.user
if user is None or user.user_name is None:
raise PermissionException('', '当前用户信息不可用')
return user.user_name
@staticmethod
def _assert_active(batch: Any | None) -> None:
if batch is None:
raise ServiceWarning('', '测试批次不存在')
status = batch.status
if status == TestBatchStatus.CLOSED.value:
raise ServiceWarning('', '测试批次已关闭')
if status == TestBatchStatus.PURGED.value:
raise ServiceWarning('', '测试批次已清理')
if status != TestBatchStatus.ACTIVE.value:
raise ServiceWarning('', '测试批次状态异常')
@staticmethod
def _assert_token(batch: object, raw_token: str, now: datetime) -> None:
supplied_hash = hashlib.sha256(raw_token.encode()).hexdigest()
stored_hash = getattr(batch, 'preview_token_hash', None)
comparable_hash = stored_hash if isinstance(stored_hash, str) else '0' * 64
if not hmac.compare_digest(comparable_hash, supplied_hash):
raise ServiceWarning('', '预览令牌无效')
if getattr(batch, 'preview_action', None) != 'ADOPT':
raise ServiceWarning('', '预览令牌无效')
expires_at = getattr(batch, 'preview_expires_at', None)
if expires_at is None or expires_at <= now:
raise ServiceWarning('', '预览令牌已过期')
@classmethod
async def preview(
cls,
db: AsyncSession,
batch_id: int,
command: AdoptionPreviewRequestModel,
current_user: CurrentUserModel,
) -> TestBatchPreviewModel:
cls._assert_admin(current_user)
user_name = cls._user_name(current_user)
try:
batch = await TestBatchDao.get_batch_for_update(db, batch_id)
cls._assert_active(batch)
selection = await TestBatchGraph.compute_closure(db, command.roots)
if await TestBatchAdoptionDao.find_tagged_record(db, selection) is not None:
raise ServiceWarning('', '存在记录已属于其他测试批次')
display_keys = await TestBatchAdoptionDao.list_display_keys(db, selection)
now = datetime.now()
expires_at = now + timedelta(seconds=600)
raw_token, token_hash = issue_preview_token()
await TestBatchDao.store_preview(
db,
batch,
action='ADOPT',
token_hash=token_hash,
scope_hash=selection_hash(selection),
expires_at=expires_at,
update_by=user_name,
update_time=now,
)
await db.commit()
impacts = [
BatchImpactModel(
table_name=table_name,
label=BATCH_LABELS[table_name],
count=len(selection[table_name]),
keys=display_keys[table_name],
)
for table_name in BATCH_MODELS
]
return TestBatchPreviewModel(
batch_id=batch_id,
batch_name=batch.batch_name,
preview_token=raw_token,
expires_at=expires_at,
total_count=sum(item.count for item in impacts),
impacts=impacts,
)
except Exception:
await db.rollback()
raise
@classmethod
async def execute(
cls,
db: AsyncSession,
batch_id: int,
command: AdoptionExecuteRequestModel,
current_user: CurrentUserModel,
) -> CrudResponseModel:
cls._assert_admin(current_user)
user_name = cls._user_name(current_user)
try:
batch = await TestBatchDao.get_batch_for_update(db, batch_id)
cls._assert_active(batch)
if command.batch_name != batch.batch_name:
raise ServiceWarning('', '测试批次名称不匹配')
cls._assert_token(batch, command.preview_token, datetime.now())
await TestBatchAdoptionDao.lock_roots(db, command.roots)
first_selection = await TestBatchGraph.compute_closure(db, command.roots)
await TestBatchAdoptionDao.lock_selection(db, first_selection)
locked_selection = await TestBatchGraph.compute_closure(db, command.roots)
expected_scope_hash = batch.preview_scope_hash
if (
selection_hash(first_selection) != expected_scope_hash
or selection_hash(locked_selection) != expected_scope_hash
):
raise ServiceWarning('', '预览范围已变化')
if await TestBatchAdoptionDao.find_tagged_record(db, locked_selection) is not None:
raise ServiceWarning('', '存在记录已属于其他测试批次')
for table_name in sorted(locked_selection):
await TestBatchAdoptionDao.set_test_batch_id(
db,
table_name,
sorted(locked_selection[table_name]),
batch_id,
)
now = datetime.now()
await TestBatchDao.clear_preview(db, batch)
batch.update_by = user_name
batch.update_time = now
await db.commit()
return CrudResponseModel(is_success=True, message='旧测试数据纳入成功')
except Exception:
await db.rollback()
raise