feat(tplus): 新增 T+ 只读同步服务与同步管理
This commit is contained in:
1 parent
78bef43c06
commit
4fe5916c2c
43 files changed
+3309
-388
No files matched your search
@@ -0,0 +1,122 @@
|
||||
"""同步管理只读查询;只公开受控元数据,不公开源数据和原异常。"""
|
||||
|
||||
from app.core.exceptions import NotFoundError
|
||||
from app.core.response import PageResult
|
||||
from app.core.tplus_db import resolve_tplus_settings
|
||||
from app.repositories import tplus_repository as repository
|
||||
from app.schemas.tplus import (
|
||||
TPlusBatchDetailOut,
|
||||
TPlusBatchOut,
|
||||
TPlusErrorOut,
|
||||
TPlusOverviewOut,
|
||||
)
|
||||
from app.services.tplus import task_service
|
||||
from app.services.tplus.mapping import DETAIL_MAPPINGS, TPLUS_MAPPINGS
|
||||
from app.services.tplus.relationship_audit import public_report
|
||||
|
||||
ERROR_MESSAGES = {
|
||||
"READ_ERROR": "源数据读取失败,请检查只读连接和字段映射",
|
||||
"STAGE_ERROR": "原始数据保存失败,请核对字段类型后重试",
|
||||
"INTERRUPTED": "执行进程中断,可重新读取,已保存数据会自动去重",
|
||||
"EXECUTION_ERROR": "任务执行失败,请检查只读连接和字段映射",
|
||||
"TYPE_UNRESOLVED": "关联核验所需的单据类型缺失或不唯一",
|
||||
}
|
||||
_ENTITY_TYPES = {item.entity_type for item in (*TPLUS_MAPPINGS, *DETAIL_MAPPINGS)}
|
||||
|
||||
|
||||
def batch_out(batch, active_parents: set[str]) -> TPlusBatchOut:
|
||||
"""只使用元数据字段组装输出,历史批次范围保持未知。"""
|
||||
entities = batch.entity_types_json
|
||||
scope_valid = (
|
||||
isinstance(entities, list)
|
||||
and bool(entities)
|
||||
and all(isinstance(item, str) and item in _ENTITY_TYPES for item in entities)
|
||||
)
|
||||
return TPlusBatchOut(
|
||||
batch_no=batch.batch_no,
|
||||
task_type="AUDIT" if batch.sync_mode == "AUDIT" else "READ",
|
||||
status=batch.status,
|
||||
entity_types=entities if scope_valid else None,
|
||||
source_record_count=batch.source_record_count,
|
||||
staged_record_count=batch.staged_record_count,
|
||||
skipped_record_count=batch.skipped_record_count,
|
||||
failed_record_count=batch.failed_record_count,
|
||||
parent_batch_no=batch.parent_batch_no,
|
||||
create_time=batch.create_time,
|
||||
started_at=batch.started_at,
|
||||
finished_at=batch.finished_at,
|
||||
can_retry=batch.status in ("FAILED", "PARTIAL")
|
||||
and (batch.sync_mode == "AUDIT" or (batch.sync_mode == "FULL" and scope_valid))
|
||||
and batch.batch_no not in active_parents,
|
||||
error_summary="任务有异常,请查看异常明细"
|
||||
if batch.status in ("FAILED", "PARTIAL")
|
||||
else None,
|
||||
)
|
||||
|
||||
|
||||
def get_batch(session, number: str) -> TPlusBatchDetailOut:
|
||||
"""按需加载报告,未找到时统一返回 404。"""
|
||||
batch = repository.get_batch(session, number, with_result=True)
|
||||
if batch is None:
|
||||
raise NotFoundError("同步批次不存在")
|
||||
out = batch_out(batch, repository.active_retry_parents(session, [number]))
|
||||
return TPlusBatchDetailOut(
|
||||
**out.model_dump(), audit_result=public_report(batch.audit_result_json)
|
||||
)
|
||||
|
||||
|
||||
def list_batches(session, query):
|
||||
"""分页批次并批量查询重试状态。"""
|
||||
rows, total = repository.list_batches(session, query)
|
||||
active = repository.active_retry_parents(session, [row.batch_no for row in rows])
|
||||
return PageResult[TPlusBatchOut](
|
||||
records=[batch_out(row, active) for row in rows],
|
||||
total=total,
|
||||
page_num=query.page_num,
|
||||
page_size=query.page_size,
|
||||
)
|
||||
|
||||
|
||||
def _safe_id(value) -> str | None:
|
||||
if isinstance(value, str) and value.isascii() and value.isdigit() and len(value) <= 10:
|
||||
return value if int(value) <= 2147483647 else None
|
||||
return None
|
||||
|
||||
|
||||
def list_errors(session, number, query):
|
||||
"""错误按编码生成固定说明,避免驱动异常泄漏连接参数。"""
|
||||
if repository.get_batch(session, number) is None:
|
||||
raise NotFoundError("同步批次不存在")
|
||||
rows, total = repository.list_errors(session, number, query)
|
||||
records = [
|
||||
TPlusErrorOut(
|
||||
id=row.id,
|
||||
entity_type=row.entity_type if row.entity_type in _ENTITY_TYPES else "system",
|
||||
source_id=_safe_id(row.source_id),
|
||||
error_code=row.error_code if row.error_code in ERROR_MESSAGES else "UNKNOWN",
|
||||
error_message=ERROR_MESSAGES.get(row.error_code, "任务未完成,请核对配置后重试"),
|
||||
retry_count=row.retry_count,
|
||||
create_time=row.create_time,
|
||||
)
|
||||
for row in rows
|
||||
]
|
||||
return PageResult[TPlusErrorOut](
|
||||
records=records, total=total, page_num=query.page_num, page_size=query.page_size
|
||||
)
|
||||
|
||||
|
||||
def overview(session) -> TPlusOverviewOut:
|
||||
"""只校验配置,不在页面查询期间建立 T+ 连接。"""
|
||||
try:
|
||||
resolve_tplus_settings(task_service.get_settings())
|
||||
configured, message = True, "只读配置已就绪,实际连接结果以任务执行为准"
|
||||
except RuntimeError:
|
||||
configured, message = False, "T+ 只读配置未就绪,请联系管理员配置"
|
||||
latest = repository.latest_audit(session)
|
||||
return TPlusOverviewOut(
|
||||
is_configured=configured,
|
||||
configuration_message=message,
|
||||
status_counts=repository.status_counts(session),
|
||||
total_staged_records=repository.total_staged_records(session),
|
||||
latest_audit=public_report(latest.audit_result_json) if latest else None,
|
||||
)
|
||||
Reference in new issue
Block a user