335 lines
11 KiB
Python
335 lines
11 KiB
Python
from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, UploadFile
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.db import get_db
|
|
from app.deps import get_current_user, require_admin
|
|
from app.integrations.adp_client import AdpClient
|
|
from app.integrations.storage_client import StorageClient
|
|
from app.models import Document, User
|
|
from app.schemas.document import DocumentCreate, DocumentResponse, DocumentUpdate
|
|
from app.services.audit_service import write_audit
|
|
from app.services.document_service import get_or_create_tags, serialize_document
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("", response_model=list[DocumentResponse])
|
|
def list_documents(db: Session = Depends(get_db), _: User = Depends(require_admin)) -> list[dict]:
|
|
documents = db.scalars(select(Document).order_by(Document.id.desc())).all()
|
|
return [serialize_document(document) for document in documents]
|
|
|
|
|
|
@router.get("/adp")
|
|
def list_adp_documents(
|
|
page: int = 1,
|
|
page_size: int = 50,
|
|
query: str = "",
|
|
cate_biz_id: str = "",
|
|
user: User = Depends(get_current_user),
|
|
) -> dict:
|
|
try:
|
|
result = AdpClient().list_documents(
|
|
page=page,
|
|
page_size=page_size,
|
|
query=query,
|
|
cate_biz_id=cate_biz_id,
|
|
)
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP ListDoc 失败: {exc}")
|
|
|
|
is_admin = user.is_superuser or any(
|
|
role.code in {"admin", "system_admin"} for role in user.roles
|
|
)
|
|
if is_admin:
|
|
return result
|
|
|
|
# 从当前单位起向上收集单位名,用作匹配对象
|
|
unit_chain: list[str] = []
|
|
seen_ids: set[int] = set()
|
|
current_unit = user.unit
|
|
while current_unit is not None and current_unit.id not in seen_ids:
|
|
seen_ids.add(current_unit.id)
|
|
name = (current_unit.name or "").strip()
|
|
if name:
|
|
unit_chain.append(name)
|
|
current_unit = current_unit.parent
|
|
|
|
filtered: list[dict] = []
|
|
for doc in result.get("list") or []:
|
|
# 标签值先去掉"可见"再判断
|
|
bases: set[str] = set()
|
|
for attr in (doc.get("attr_labels") or []):
|
|
for label in (attr.get("labels") or []):
|
|
name = (label.get("label_name") or "").strip()
|
|
if not name:
|
|
continue
|
|
bases.add(name.replace("可见", ""))
|
|
if "所有人" in bases:
|
|
filtered.append(doc)
|
|
continue
|
|
matched = False
|
|
for unit_name in unit_chain:
|
|
if any(unit_name in base for base in bases):
|
|
matched = True
|
|
break
|
|
if matched:
|
|
filtered.append(doc)
|
|
|
|
return {"total": len(filtered), "list": filtered}
|
|
|
|
|
|
@router.get("/adp/categories")
|
|
def list_adp_categories(_: User = Depends(get_current_user)) -> dict:
|
|
try:
|
|
return {"list": AdpClient().list_categories()}
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP ListDocCate 失败: {exc}")
|
|
|
|
|
|
@router.post("/adp/categories")
|
|
def create_adp_category(
|
|
payload: dict = Body(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
name = (payload.get("name") or "").strip()
|
|
parent_biz_id = (payload.get("parent_biz_id") or "").strip()
|
|
if not name:
|
|
raise HTTPException(status_code=400, detail="分类名称不能为空")
|
|
try:
|
|
result = AdpClient().create_category(name=name, parent_biz_id=parent_biz_id)
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP CreateDocCate 失败: {exc}")
|
|
write_audit(
|
|
db,
|
|
"document.adp_cate_create",
|
|
actor_id=admin.id,
|
|
target_type="adp_category",
|
|
target_id=result.get("cate_biz_id") or "",
|
|
detail={"name": name, "parent_biz_id": parent_biz_id},
|
|
)
|
|
db.commit()
|
|
return result
|
|
|
|
|
|
@router.patch("/adp/categories/{cate_biz_id}")
|
|
def modify_adp_category(
|
|
cate_biz_id: str,
|
|
payload: dict = Body(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
name = (payload.get("name") or "").strip()
|
|
if not name:
|
|
raise HTTPException(status_code=400, detail="分类名称不能为空")
|
|
try:
|
|
AdpClient().modify_category(cate_biz_id, name)
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP ModifyDocCate 失败: {exc}")
|
|
write_audit(
|
|
db,
|
|
"document.adp_cate_modify",
|
|
actor_id=admin.id,
|
|
target_type="adp_category",
|
|
target_id=cate_biz_id,
|
|
detail={"name": name},
|
|
)
|
|
db.commit()
|
|
return {"cate_biz_id": cate_biz_id, "name": name}
|
|
|
|
|
|
@router.delete("/adp/categories/{cate_biz_id}")
|
|
def delete_adp_category(
|
|
cate_biz_id: str,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
try:
|
|
AdpClient().delete_category(cate_biz_id)
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP DeleteDocCate 失败: {exc}")
|
|
write_audit(
|
|
db,
|
|
"document.adp_cate_delete",
|
|
actor_id=admin.id,
|
|
target_type="adp_category",
|
|
target_id=cate_biz_id,
|
|
)
|
|
db.commit()
|
|
return {"cate_biz_id": cate_biz_id, "deleted": True}
|
|
|
|
|
|
@router.delete("/adp/{doc_biz_id}")
|
|
def delete_from_adp(
|
|
doc_biz_id: str,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
if not doc_biz_id:
|
|
raise HTTPException(status_code=400, detail="缺少 DocBizId")
|
|
try:
|
|
AdpClient().delete_documents([doc_biz_id])
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP DeleteDoc 失败: {exc}")
|
|
write_audit(
|
|
db,
|
|
"document.adp_delete",
|
|
actor_id=admin.id,
|
|
target_type="adp_document",
|
|
target_id=doc_biz_id,
|
|
)
|
|
db.commit()
|
|
return {"doc_biz_id": doc_biz_id, "deleted": True}
|
|
|
|
|
|
@router.post("/adp/upload")
|
|
def upload_to_adp(
|
|
file: UploadFile = File(...),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
if not file.filename:
|
|
raise HTTPException(status_code=400, detail="请选择文件")
|
|
file_bytes = file.file.read()
|
|
try:
|
|
result = AdpClient().upload_document(file.filename, file_bytes)
|
|
except RuntimeError as exc:
|
|
raise HTTPException(status_code=400, detail=str(exc))
|
|
except Exception as exc:
|
|
raise HTTPException(status_code=502, detail=f"调用 ADP SaveDoc 失败: {exc}")
|
|
if result.get("error_msg"):
|
|
raise HTTPException(status_code=400, detail=result["error_msg"])
|
|
write_audit(
|
|
db,
|
|
"document.adp_upload",
|
|
actor_id=admin.id,
|
|
target_type="adp_document",
|
|
target_id=str(result.get("doc_biz_id") or ""),
|
|
detail={"filename": file.filename, "size": len(file_bytes)},
|
|
)
|
|
db.commit()
|
|
return result
|
|
|
|
|
|
@router.post("", response_model=DocumentResponse)
|
|
def create_document(
|
|
payload: DocumentCreate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
document = Document(
|
|
title=payload.title,
|
|
version=payload.version,
|
|
secrecy_level=payload.secrecy_level,
|
|
owner_dept=payload.owner_dept,
|
|
description=payload.description,
|
|
status="pending",
|
|
created_by_id=admin.id,
|
|
)
|
|
document.tags = get_or_create_tags(db, payload.tags)
|
|
db.add(document)
|
|
db.flush()
|
|
document.adp_doc_id = f"mock-adp-doc-{document.id}"
|
|
document.status = "parsed"
|
|
write_audit(db, "document.create", actor_id=admin.id, target_type="document", target_id=str(document.id))
|
|
db.commit()
|
|
db.refresh(document)
|
|
return serialize_document(document)
|
|
|
|
|
|
@router.post("/upload", response_model=DocumentResponse)
|
|
def upload_document(
|
|
file: UploadFile = File(...),
|
|
tags: str = Form(default=""),
|
|
secrecy_level: str = Form(default="internal"),
|
|
owner_dept: str = Form(default="财务部"),
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
if not file.filename:
|
|
raise HTTPException(status_code=400, detail="请选择文件")
|
|
storage_key = StorageClient().save_upload(file)
|
|
document = Document(
|
|
title=file.filename,
|
|
storage_key=storage_key,
|
|
secrecy_level=secrecy_level,
|
|
owner_dept=owner_dept,
|
|
status="pending",
|
|
created_by_id=admin.id,
|
|
)
|
|
tag_names = [tag.strip() for tag in tags.split(",") if tag.strip()]
|
|
document.tags = get_or_create_tags(db, tag_names)
|
|
db.add(document)
|
|
db.flush()
|
|
document.adp_doc_id = f"mock-adp-doc-{document.id}"
|
|
document.status = "parsed"
|
|
write_audit(
|
|
db,
|
|
"document.upload",
|
|
actor_id=admin.id,
|
|
target_type="document",
|
|
target_id=str(document.id),
|
|
detail={"filename": file.filename, "storage_key": storage_key},
|
|
)
|
|
db.commit()
|
|
db.refresh(document)
|
|
return serialize_document(document)
|
|
|
|
|
|
@router.patch("/{document_id}", response_model=DocumentResponse)
|
|
def update_document(
|
|
document_id: int,
|
|
payload: DocumentUpdate,
|
|
db: Session = Depends(get_db),
|
|
admin: User = Depends(require_admin),
|
|
) -> dict:
|
|
document = db.get(Document, document_id)
|
|
if not document:
|
|
raise HTTPException(status_code=404, detail="文档不存在")
|
|
for field in ("title", "version", "secrecy_level", "status", "owner_dept", "description"):
|
|
value = getattr(payload, field)
|
|
if value is not None:
|
|
setattr(document, field, value)
|
|
if payload.tags is not None:
|
|
document.tags = get_or_create_tags(db, payload.tags)
|
|
write_audit(db, "document.update", actor_id=admin.id, target_type="document", target_id=str(document.id))
|
|
db.commit()
|
|
db.refresh(document)
|
|
return serialize_document(document)
|
|
|
|
|
|
@router.delete("/{document_id}")
|
|
def delete_document(document_id: int, db: Session = Depends(get_db), admin: User = Depends(require_admin)) -> dict:
|
|
document = db.get(Document, document_id)
|
|
if not document:
|
|
raise HTTPException(status_code=404, detail="文档不存在")
|
|
document.status = "deleted"
|
|
write_audit(db, "document.delete", actor_id=admin.id, target_type="document", target_id=str(document.id))
|
|
db.commit()
|
|
return {"message": "文档已删除"}
|
|
|
|
|
|
@router.post("/{document_id}/retry-parse", response_model=DocumentResponse)
|
|
def retry_parse(document_id: int, db: Session = Depends(get_db), admin: User = Depends(require_admin)) -> dict:
|
|
document = db.get(Document, document_id)
|
|
if not document:
|
|
raise HTTPException(status_code=404, detail="文档不存在")
|
|
document.status = "parsed"
|
|
write_audit(db, "document.retry_parse", actor_id=admin.id, target_type="document", target_id=str(document.id))
|
|
db.commit()
|
|
db.refresh(document)
|
|
return serialize_document(document)
|