feat: add document type root management
This commit is contained in:
@@ -26,6 +26,9 @@ from fastapi_modules.fastapi_leaudit.domian.vo.documentVo import (
|
||||
DocumentListItemVO,
|
||||
DocumentListPageVO,
|
||||
DocumentStatusItemVO,
|
||||
DocumentTypeRootCreateDTO,
|
||||
DocumentTypeRootItemVO,
|
||||
DocumentTypeRootUpdateDTO,
|
||||
DocumentUpdateDTO,
|
||||
DocumentTypeCreateDTO,
|
||||
DocumentTypeItemVO,
|
||||
@@ -1185,6 +1188,97 @@ class DocumentServiceImpl(IDocumentService):
|
||||
await Session.execute(text("UPDATE leaudit_rule_type_bindings SET deleted_at = NOW() WHERE doc_type_id = :id AND deleted_at IS NULL"), {"id": Id})
|
||||
await Session.commit()
|
||||
|
||||
async def ListDocumentTypeRoots(self, EntryModuleId: int | None = None) -> list[DocumentTypeRootItemVO]:
|
||||
"""获取一级文档类型(业务大类)列表。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
rows = await self._queryDocumentTypeRoots(Session, EntryModuleId=EntryModuleId)
|
||||
return [self._toDocumentTypeRootVo(row) for row in rows]
|
||||
|
||||
async def GetDocumentTypeRoot(self, Id: int) -> DocumentTypeRootItemVO:
|
||||
"""获取一级文档类型(业务大类)详情。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
rows = await self._queryDocumentTypeRoots(Session, Ids=[Id])
|
||||
if not rows:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "一级文档类型不存在")
|
||||
return self._toDocumentTypeRootVo(rows[0])
|
||||
|
||||
async def CreateDocumentTypeRoot(self, Body: DocumentTypeRootCreateDTO) -> DocumentTypeRootItemVO:
|
||||
"""创建一级文档类型(业务大类)。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
await self._ensureDocumentTypeRootCodeUnique(Session, Body.code.strip(), None)
|
||||
root_id = (
|
||||
await Session.execute(
|
||||
text(
|
||||
"""
|
||||
INSERT INTO leaudit_evaluation_point_groups
|
||||
(pid, name, code, description, document_type_id, entry_module_id, sort_order, is_enabled, created_at, updated_at)
|
||||
VALUES
|
||||
(0, :name, :code, :description, NULL, :entry_module_id, :sort_order, :is_enabled, NOW(), NOW())
|
||||
RETURNING id
|
||||
"""
|
||||
),
|
||||
{
|
||||
"name": Body.name.strip(),
|
||||
"code": Body.code.strip(),
|
||||
"description": Body.description.strip() or None,
|
||||
"entry_module_id": Body.entryModuleId,
|
||||
"sort_order": Body.sortOrder,
|
||||
"is_enabled": Body.isEnabled,
|
||||
},
|
||||
)
|
||||
).scalar_one()
|
||||
await Session.commit()
|
||||
return await self.GetDocumentTypeRoot(int(root_id))
|
||||
|
||||
async def UpdateDocumentTypeRoot(self, Id: int, Body: DocumentTypeRootUpdateDTO) -> DocumentTypeRootItemVO:
|
||||
"""更新一级文档类型(业务大类)。"""
|
||||
async with GetAsyncSession() as Session:
|
||||
current = (
|
||||
await Session.execute(
|
||||
text(
|
||||
"""
|
||||
SELECT 1
|
||||
FROM leaudit_evaluation_point_groups
|
||||
WHERE id = :id
|
||||
AND deleted_at IS NULL
|
||||
AND COALESCE(pid, 0) = 0
|
||||
"""
|
||||
),
|
||||
{"id": Id},
|
||||
)
|
||||
).first()
|
||||
if not current:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_404_NOT_FOUND, "一级文档类型不存在")
|
||||
|
||||
providedFields = set(getattr(Body, "model_fields_set", set()))
|
||||
sets: list[str] = []
|
||||
params: dict[str, object] = {"id": Id}
|
||||
if "name" in providedFields and Body.name is not None:
|
||||
sets.append("name = :name")
|
||||
params["name"] = Body.name.strip()
|
||||
if "description" in providedFields:
|
||||
sets.append("description = :description")
|
||||
params["description"] = Body.description.strip() if Body.description is not None else None
|
||||
if params["description"] == "":
|
||||
params["description"] = None
|
||||
if "entryModuleId" in providedFields:
|
||||
sets.append("entry_module_id = :entry_module_id")
|
||||
params["entry_module_id"] = Body.entryModuleId
|
||||
if "isEnabled" in providedFields and Body.isEnabled is not None:
|
||||
sets.append("is_enabled = :is_enabled")
|
||||
params["is_enabled"] = Body.isEnabled
|
||||
if "sortOrder" in providedFields and Body.sortOrder is not None:
|
||||
sets.append("sort_order = :sort_order")
|
||||
params["sort_order"] = Body.sortOrder
|
||||
if sets:
|
||||
sets.append("updated_at = NOW()")
|
||||
await Session.execute(
|
||||
text(f"UPDATE leaudit_evaluation_point_groups SET {', '.join(sets)} WHERE id = :id"),
|
||||
params,
|
||||
)
|
||||
await Session.commit()
|
||||
return await self.GetDocumentTypeRoot(Id)
|
||||
|
||||
async def _queryDocumentTypes(self, Session, Ids: list[int] | None = None, EntryModuleId: int | None = None):
|
||||
"""查询文档类型及其规则绑定。"""
|
||||
if Ids:
|
||||
@@ -1250,6 +1344,93 @@ class DocumentServiceImpl(IDocumentService):
|
||||
bindingsMap.setdefault(int(b[0]), []).append(int(b[1]))
|
||||
return rows, bindingsMap
|
||||
|
||||
async def _queryDocumentTypeRoots(self, Session, Ids: list[int] | None = None, EntryModuleId: int | None = None):
|
||||
filters = ["g.deleted_at IS NULL", "COALESCE(g.pid, 0) = 0"]
|
||||
params: dict[str, object] = {}
|
||||
if Ids:
|
||||
filters.append("g.id = ANY(:ids)")
|
||||
params["ids"] = Ids
|
||||
if EntryModuleId is not None:
|
||||
filters.append("g.entry_module_id = :entry_module_id")
|
||||
params["entry_module_id"] = EntryModuleId
|
||||
|
||||
where_clause = " AND ".join(filters)
|
||||
rows = (
|
||||
await Session.execute(
|
||||
text(
|
||||
f"""
|
||||
SELECT
|
||||
g.id,
|
||||
g.name,
|
||||
g.code,
|
||||
g.description,
|
||||
g.entry_module_id,
|
||||
em.name AS entry_module_name,
|
||||
g.is_enabled,
|
||||
COALESCE(child_stats.child_group_count, 0) AS child_group_count,
|
||||
COALESCE(rule_stats.rule_set_count, 0) AS rule_set_count,
|
||||
COALESCE(rule_stats.rule_set_ids, ARRAY[]::bigint[]) AS rule_set_ids
|
||||
FROM leaudit_evaluation_point_groups g
|
||||
LEFT JOIN leaudit_entry_modules em ON em.id = g.entry_module_id
|
||||
LEFT JOIN (
|
||||
SELECT pid, COUNT(*)::int AS child_group_count
|
||||
FROM leaudit_evaluation_point_groups
|
||||
WHERE deleted_at IS NULL AND COALESCE(pid, 0) <> 0
|
||||
GROUP BY pid
|
||||
) child_stats ON child_stats.pid = g.id
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
child.pid,
|
||||
COUNT(DISTINCT rgb.rule_set_id)::int AS rule_set_count,
|
||||
ARRAY_AGG(DISTINCT rgb.rule_set_id) FILTER (WHERE rgb.rule_set_id IS NOT NULL) AS rule_set_ids
|
||||
FROM leaudit_evaluation_point_groups child
|
||||
LEFT JOIN leaudit_rule_group_bindings rgb
|
||||
ON rgb.group_id = child.id
|
||||
AND rgb.deleted_at IS NULL
|
||||
WHERE child.deleted_at IS NULL
|
||||
AND COALESCE(child.pid, 0) <> 0
|
||||
GROUP BY child.pid
|
||||
) rule_stats ON rule_stats.pid = g.id
|
||||
WHERE {where_clause}
|
||||
ORDER BY COALESCE(g.sort_order, 0) ASC, g.id ASC
|
||||
"""
|
||||
),
|
||||
params,
|
||||
)
|
||||
).mappings().all()
|
||||
return rows
|
||||
|
||||
def _toDocumentTypeRootVo(self, row) -> DocumentTypeRootItemVO:
|
||||
rule_set_ids = [int(item) for item in (row.get("rule_set_ids") or []) if item is not None]
|
||||
return DocumentTypeRootItemVO(
|
||||
id=int(row["id"]),
|
||||
name=str(row["name"] or ""),
|
||||
code=str(row["code"] or ""),
|
||||
description=row.get("description"),
|
||||
entryModuleId=int(row["entry_module_id"]) if row.get("entry_module_id") is not None else None,
|
||||
entryModuleName=row.get("entry_module_name"),
|
||||
isEnabled=bool(row.get("is_enabled", True)),
|
||||
childGroupCount=int(row.get("child_group_count") or 0),
|
||||
ruleSetCount=int(row.get("rule_set_count") or 0),
|
||||
ruleSetIds=rule_set_ids,
|
||||
)
|
||||
|
||||
async def _ensureDocumentTypeRootCodeUnique(self, Session, Code: str, CurrentId: int | None) -> None:
|
||||
sql = """
|
||||
SELECT 1
|
||||
FROM leaudit_evaluation_point_groups
|
||||
WHERE deleted_at IS NULL
|
||||
AND COALESCE(pid, 0) = 0
|
||||
AND code = :code
|
||||
"""
|
||||
params: dict[str, object] = {"code": Code}
|
||||
if CurrentId is not None:
|
||||
sql += " AND id <> :current_id"
|
||||
params["current_id"] = CurrentId
|
||||
exists = (await Session.execute(text(sql), params)).first()
|
||||
if exists:
|
||||
raise LeauditException(StatusCodeEnum.HTTP_409_CONFLICT, f"一级文档类型编码 {Code} 已存在")
|
||||
|
||||
async def _loadDocumentColumns(self, Session) -> set[str]:
|
||||
"""读取 leaudit_documents 当前真实列,兼容不同环境的渐进式字段上线。"""
|
||||
rows = (
|
||||
|
||||
Reference in New Issue
Block a user