290 lines
11 KiB
Python
290 lines
11 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
"""报表版本管理服务"""
|
|
import copy
|
|
import json
|
|
import logging
|
|
from typing import Any, Dict, List, Optional
|
|
|
|
from sqlalchemy import select, update, func, and_
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from online_dev.report_manager.model import ReportTemplate, ReportVersion, ReportDataset
|
|
from online_dev.report_manager.enums import ReportVersionState
|
|
from online_dev.report_manager.exceptions import ReportServiceException
|
|
from online_dev.report_manager.constants import default_snapshot, default_cells
|
|
from online_dev.report_manager.dataset_bridge import ReportDatasetBridge
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ReportVersionService:
|
|
@staticmethod
|
|
async def get(db: AsyncSession, version_id: str) -> ReportVersion:
|
|
stmt = select(ReportVersion).where(
|
|
ReportVersion.id == version_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
ver = (await db.execute(stmt)).scalar_one_or_none()
|
|
if not ver:
|
|
raise ReportServiceException(f"版本不存在: {version_id}")
|
|
return ver
|
|
|
|
@staticmethod
|
|
async def list_by_template(db: AsyncSession, template_id: str) -> List[ReportVersion]:
|
|
stmt = (
|
|
select(ReportVersion)
|
|
.where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
.order_by(ReportVersion.version.desc())
|
|
)
|
|
return list((await db.execute(stmt)).scalars().all())
|
|
|
|
@staticmethod
|
|
async def get_active(db: AsyncSession, template_id: str) -> Optional[ReportVersion]:
|
|
stmt = select(ReportVersion).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.state == ReportVersionState.ACTIVE,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
return (await db.execute(stmt)).scalar_one_or_none()
|
|
|
|
@staticmethod
|
|
async def get_designing_or_latest(db: AsyncSession, template_id: str) -> Optional[ReportVersion]:
|
|
stmt = select(ReportVersion).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.is_deleted == False,
|
|
).order_by(
|
|
ReportVersion.state.asc(),
|
|
ReportVersion.version.desc(),
|
|
)
|
|
versions = list((await db.execute(stmt)).scalars().all())
|
|
for v in versions:
|
|
if v.state == ReportVersionState.DESIGNING:
|
|
return v
|
|
return versions[0] if versions else None
|
|
|
|
@staticmethod
|
|
def _parse_json_field(value: Any, default: Any) -> Any:
|
|
if value is None:
|
|
return default
|
|
if isinstance(value, str):
|
|
try:
|
|
return json.loads(value)
|
|
except json.JSONDecodeError:
|
|
return default
|
|
return value
|
|
|
|
@staticmethod
|
|
async def save(db: AsyncSession, data: Dict[str, Any], user_id: str = None) -> Dict[str, Any]:
|
|
from online_dev.report_manager.service import ReportService
|
|
|
|
template_id = data.get("id")
|
|
tpl = await ReportService.get(db, template_id)
|
|
|
|
version_id = data.get("version_id")
|
|
save_type = data.get("type", 0)
|
|
|
|
snapshot = ReportVersionService._parse_json_field(data.get("snapshot"), default_snapshot())
|
|
cells = ReportVersionService._parse_json_field(data.get("cells"), default_cells())
|
|
query_list = ReportVersionService._parse_json_field(data.get("query_list"), [])
|
|
sort_list = ReportVersionService._parse_json_field(data.get("sort_list"), [])
|
|
column_list = ReportVersionService._parse_json_field(data.get("column_list"), [])
|
|
fence_list = ReportVersionService._parse_json_field(data.get("fence_list"), [])
|
|
convert_config = ReportVersionService._parse_json_field(data.get("convert_config"), {})
|
|
data_set_list = data.get("data_set_list") or []
|
|
|
|
if version_id:
|
|
version = await ReportVersionService.get(db, version_id)
|
|
if version.template_id != template_id:
|
|
raise ReportServiceException("版本与模板不匹配")
|
|
if version.state != ReportVersionState.DESIGNING:
|
|
raise ReportServiceException("只能编辑设计中版本")
|
|
else:
|
|
designing = await ReportVersionService.get_designing_or_latest(db, template_id)
|
|
if designing and designing.state == ReportVersionState.DESIGNING:
|
|
version = designing
|
|
else:
|
|
max_ver = (
|
|
await db.execute(
|
|
select(func.max(ReportVersion.version)).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
)
|
|
).scalar() or 0
|
|
version = ReportVersion(
|
|
template_id=template_id,
|
|
version=max_ver + 1,
|
|
state=ReportVersionState.DESIGNING,
|
|
snapshot=default_snapshot(),
|
|
cells=default_cells(),
|
|
sys_creator_id=user_id,
|
|
sys_modifier_id=user_id,
|
|
)
|
|
db.add(version)
|
|
await db.flush()
|
|
|
|
version.snapshot = snapshot
|
|
version.cells = cells
|
|
version.query_list = query_list
|
|
version.sort_list = sort_list
|
|
version.column_list = column_list
|
|
version.fence_list = fence_list
|
|
version.convert_config = convert_config
|
|
version.sys_modifier_id = user_id
|
|
|
|
await ReportDatasetBridge.sync_datasets(db, version.id, data_set_list)
|
|
|
|
if save_type == 1:
|
|
stmt = update(ReportVersion).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.state == ReportVersionState.ACTIVE,
|
|
ReportVersion.is_deleted == False,
|
|
).values(state=ReportVersionState.ARCHIVED)
|
|
await db.execute(stmt)
|
|
version.state = ReportVersionState.ACTIVE
|
|
await db.flush()
|
|
|
|
max_ver = (
|
|
await db.execute(
|
|
select(func.max(ReportVersion.version)).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
)
|
|
).scalar() or version.version
|
|
new_designing = ReportVersion(
|
|
template_id=template_id,
|
|
version=max_ver + 1,
|
|
state=ReportVersionState.DESIGNING,
|
|
snapshot=copy.deepcopy(version.snapshot),
|
|
cells=copy.deepcopy(version.cells),
|
|
query_list=copy.deepcopy(version.query_list),
|
|
sort_list=copy.deepcopy(version.sort_list),
|
|
column_list=copy.deepcopy(version.column_list),
|
|
fence_list=copy.deepcopy(version.fence_list),
|
|
convert_config=copy.deepcopy(version.convert_config),
|
|
sys_creator_id=user_id,
|
|
sys_modifier_id=user_id,
|
|
)
|
|
db.add(new_designing)
|
|
await db.flush()
|
|
await ReportDatasetBridge.copy_datasets(db, version.id, new_designing.id)
|
|
version = new_designing
|
|
else:
|
|
version.state = ReportVersionState.DESIGNING
|
|
|
|
await db.commit()
|
|
await db.refresh(version)
|
|
return {
|
|
"template_id": template_id,
|
|
"version_id": version.id,
|
|
"state": int(version.state),
|
|
}
|
|
|
|
@staticmethod
|
|
async def get_version_detail(db: AsyncSession, version_id: str) -> Dict[str, Any]:
|
|
version = await ReportVersionService.get(db, version_id)
|
|
datasets = await ReportDatasetBridge.list_by_version(db, version_id)
|
|
return {
|
|
"version": version,
|
|
"datasets": datasets,
|
|
}
|
|
|
|
@staticmethod
|
|
async def delete_version(db: AsyncSession, version_id: str) -> bool:
|
|
version = await ReportVersionService.get(db, version_id)
|
|
if version.state in (ReportVersionState.ACTIVE, ReportVersionState.ARCHIVED):
|
|
raise ReportServiceException("不能删除启用中或已归档版本")
|
|
|
|
count_stmt = select(func.count(ReportVersion.id)).where(
|
|
ReportVersion.template_id == version.template_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
if (await db.execute(count_stmt)).scalar() <= 1:
|
|
raise ReportServiceException("不能删除最后一个版本")
|
|
|
|
version.is_deleted = True
|
|
await ReportDatasetBridge.delete_by_version(db, version_id)
|
|
await db.commit()
|
|
return True
|
|
|
|
@staticmethod
|
|
async def copy_version_content(
|
|
db: AsyncSession,
|
|
src_version_id: str,
|
|
new_template_id: str,
|
|
user_id: str = None,
|
|
) -> None:
|
|
src = await ReportVersionService.get(db, src_version_id)
|
|
new_ver = (
|
|
await db.execute(
|
|
select(ReportVersion).where(
|
|
ReportVersion.template_id == new_template_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
)
|
|
).scalar_one_or_none()
|
|
if not new_ver:
|
|
return
|
|
new_ver.snapshot = src.snapshot
|
|
new_ver.cells = src.cells
|
|
new_ver.query_list = src.query_list
|
|
new_ver.sort_list = src.sort_list
|
|
new_ver.column_list = src.column_list
|
|
new_ver.fence_list = src.fence_list
|
|
new_ver.convert_config = src.convert_config
|
|
await ReportDatasetBridge.copy_datasets(db, src_version_id, new_ver.id)
|
|
await db.commit()
|
|
|
|
@staticmethod
|
|
async def duplicate_version(
|
|
db: AsyncSession,
|
|
src_version_id: str,
|
|
user_id: str = None,
|
|
) -> ReportVersion:
|
|
"""复制版本为新的「设计中」版本(同模板)。"""
|
|
src = await ReportVersionService.get(db, src_version_id)
|
|
template_id = src.template_id
|
|
|
|
await db.execute(
|
|
update(ReportVersion).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.state == ReportVersionState.DESIGNING,
|
|
ReportVersion.is_deleted == False,
|
|
).values(state=ReportVersionState.ARCHIVED)
|
|
)
|
|
|
|
max_ver = (
|
|
await db.execute(
|
|
select(func.max(ReportVersion.version)).where(
|
|
ReportVersion.template_id == template_id,
|
|
ReportVersion.is_deleted == False,
|
|
)
|
|
)
|
|
).scalar() or 0
|
|
|
|
new_ver = ReportVersion(
|
|
template_id=template_id,
|
|
version=max_ver + 1,
|
|
state=ReportVersionState.DESIGNING,
|
|
snapshot=copy.deepcopy(src.snapshot),
|
|
cells=copy.deepcopy(src.cells),
|
|
query_list=copy.deepcopy(src.query_list),
|
|
sort_list=copy.deepcopy(src.sort_list),
|
|
column_list=copy.deepcopy(src.column_list),
|
|
fence_list=copy.deepcopy(src.fence_list),
|
|
convert_config=copy.deepcopy(src.convert_config),
|
|
sys_creator_id=user_id,
|
|
sys_modifier_id=user_id,
|
|
)
|
|
db.add(new_ver)
|
|
await db.flush()
|
|
await ReportDatasetBridge.copy_datasets(db, src.id, new_ver.id)
|
|
await db.commit()
|
|
await db.refresh(new_ver)
|
|
return new_ver
|