fix(审计): 移除共享库审计与预览噪声
This commit is contained in:
@@ -16,9 +16,8 @@ from sqlalchemy import and_, func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.config import settings
|
||||
from app.core.deps import get_operator_role_label, is_system_admin
|
||||
from app.core.deps import is_system_admin
|
||||
from app.core.project_permissions import role_has_api_permission
|
||||
from app.crud import audit as audit_crud
|
||||
from app.crud import member as member_crud
|
||||
from app.models.collaboration import (
|
||||
CollaborationEditRequest,
|
||||
@@ -427,23 +426,6 @@ async def require_ownership_transfer(db: AsyncSession, item: CollaborationFile,
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="您没有转让此协作文件所有权的权限")
|
||||
|
||||
|
||||
async def _audit(db: AsyncSession, item: CollaborationFile, action: str, user, extra: dict | None = None) -> None:
|
||||
detail = {"file_id": str(item.id), "title": item.title}
|
||||
if extra:
|
||||
detail.update(extra)
|
||||
await audit_crud.log_action(
|
||||
db,
|
||||
study_id=item.study_id,
|
||||
entity_type="COLLABORATION_FILE",
|
||||
entity_id=item.id,
|
||||
action=action,
|
||||
detail=json.dumps(detail, ensure_ascii=False),
|
||||
operator_id=user.id,
|
||||
operator_role=await get_operator_role_label(db, item.study_id, user),
|
||||
auto_commit=False,
|
||||
)
|
||||
|
||||
|
||||
async def create_folder(db: AsyncSession, study_id: uuid.UUID, payload: CollaborationFolderCreate, user) -> CollaborationFolder:
|
||||
if payload.parent_id:
|
||||
await _folder_or_404(db, study_id, payload.parent_id)
|
||||
@@ -618,7 +600,6 @@ async def _create_file_from_bytes(
|
||||
content: bytes,
|
||||
source: str,
|
||||
user,
|
||||
source_file_id: uuid.UUID | None = None,
|
||||
) -> CollaborationFile:
|
||||
membership = await member_crud.get_member(db, study_id, user.id)
|
||||
await _require_role_assignable(db, study_id, user, membership, "MANAGER")
|
||||
@@ -637,10 +618,6 @@ async def _create_file_from_bytes(
|
||||
await db.flush()
|
||||
db.add(CollaborationMember(file_id=item.id, user_id=user.id, role="MANAGER", invited_by=user.id))
|
||||
await _persist_revision_bytes(db, item, content, source=source, created_by=user.id, original_filename=item.title)
|
||||
audit_detail = {"source": source}
|
||||
if source_file_id:
|
||||
audit_detail["source_file_id"] = str(source_file_id)
|
||||
await _audit(db, item, "COLLABORATION_FILE_CREATED", user, audit_detail)
|
||||
await db.commit()
|
||||
await db.refresh(item)
|
||||
return item
|
||||
@@ -775,11 +752,6 @@ async def update_file(
|
||||
db: AsyncSession, item: CollaborationFile, payload: CollaborationFileUpdate, user
|
||||
) -> CollaborationFile:
|
||||
await require_file_manager(db, item, user)
|
||||
previous_title = item.title
|
||||
previous_folder_id = item.folder_id
|
||||
previous_allow_export = item.allow_export
|
||||
previous_allow_edit_request = item.allow_edit_request
|
||||
previous_allow_sheet_structure_edit = item.allow_sheet_structure_edit
|
||||
values = payload.model_dump(exclude_unset=True)
|
||||
if "folder_id" in values and values["folder_id"]:
|
||||
await _folder_or_404(db, item.study_id, values["folder_id"])
|
||||
@@ -810,25 +782,6 @@ async def update_file(
|
||||
item.generation += 1
|
||||
for key, value in values.items():
|
||||
setattr(item, key, value)
|
||||
title_changed = item.title != previous_title
|
||||
folder_changed = item.folder_id != previous_folder_id
|
||||
if title_changed and not folder_changed:
|
||||
action = "COLLABORATION_FILE_RENAMED"
|
||||
elif folder_changed and not title_changed:
|
||||
action = "COLLABORATION_FILE_MOVED"
|
||||
else:
|
||||
action = "COLLABORATION_FILE_UPDATED"
|
||||
await _audit(db, item, action, user, {
|
||||
"previous_title": previous_title,
|
||||
"previous_folder_id": str(previous_folder_id) if previous_folder_id else None,
|
||||
"folder_id": str(item.folder_id) if item.folder_id else None,
|
||||
"previous_allow_export": previous_allow_export,
|
||||
"allow_export": item.allow_export,
|
||||
"previous_allow_edit_request": previous_allow_edit_request,
|
||||
"allow_edit_request": item.allow_edit_request,
|
||||
"previous_allow_sheet_structure_edit": previous_allow_sheet_structure_edit,
|
||||
"allow_sheet_structure_edit": item.allow_sheet_structure_edit,
|
||||
})
|
||||
await db.commit()
|
||||
await db.refresh(item)
|
||||
return item
|
||||
@@ -874,7 +827,6 @@ async def copy_file(db: AsyncSession, item: CollaborationFile, user) -> Collabor
|
||||
content=content,
|
||||
source="COPY",
|
||||
user=user,
|
||||
source_file_id=item.id,
|
||||
)
|
||||
|
||||
|
||||
@@ -885,14 +837,6 @@ async def prepare_download(db: AsyncSession, item: CollaborationFile, user) -> C
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="协作文件尚无可下载版本")
|
||||
if not Path(revision.file_uri).is_file():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="协作文件当前版本不存在")
|
||||
await _audit(
|
||||
db,
|
||||
item,
|
||||
"COLLABORATION_FILE_DOWNLOADED",
|
||||
user,
|
||||
{"revision_id": str(revision.id), "revision_no": revision.revision_no},
|
||||
)
|
||||
await db.commit()
|
||||
return revision
|
||||
|
||||
|
||||
@@ -900,7 +844,6 @@ async def move_to_trash(db: AsyncSession, item: CollaborationFile, user) -> None
|
||||
await require_file_manager(db, item, user)
|
||||
item.status = "DELETED"
|
||||
item.deleted_at = datetime.now(timezone.utc)
|
||||
await _audit(db, item, "COLLABORATION_FILE_TRASHED", user)
|
||||
await db.commit()
|
||||
|
||||
|
||||
@@ -908,7 +851,6 @@ async def restore_file(db: AsyncSession, item: CollaborationFile, user) -> Colla
|
||||
await require_file_manager(db, item, user)
|
||||
item.status = "ACTIVE"
|
||||
item.deleted_at = None
|
||||
await _audit(db, item, "COLLABORATION_FILE_RESTORED", user)
|
||||
await db.commit()
|
||||
await db.refresh(item)
|
||||
return item
|
||||
@@ -916,14 +858,10 @@ async def restore_file(db: AsyncSession, item: CollaborationFile, user) -> Colla
|
||||
|
||||
async def record_export(db: AsyncSession, item: CollaborationFile, user, file_type: str) -> None:
|
||||
await require_file_exporter(db, item, user)
|
||||
await _audit(db, item, "COLLABORATION_FILE_EXPORTED", user, {"file_type": file_type})
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def record_download(db: AsyncSession, item: CollaborationFile, user, file_type: str) -> None:
|
||||
await require_file_exporter(db, item, user)
|
||||
await _audit(db, item, "COLLABORATION_FILE_DOWNLOADED", user, {"file_type": file_type})
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def list_revisions(db: AsyncSession, item: CollaborationFile) -> list[CollaborationRevisionRead]:
|
||||
@@ -967,10 +905,6 @@ async def delete_revision(
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="当前版本不能删除")
|
||||
revision.deleted_at = datetime.now(timezone.utc)
|
||||
revision.deleted_by = user.id
|
||||
await _audit(db, item, "COLLABORATION_REVISION_DELETED", user, {
|
||||
"revision_id": str(revision.id),
|
||||
"revision_no": revision.revision_no,
|
||||
})
|
||||
await db.commit()
|
||||
|
||||
|
||||
@@ -984,13 +918,6 @@ async def update_revision(
|
||||
await require_file_editor(db, item, user)
|
||||
revision = await get_revision_or_404(db, item, revision_id)
|
||||
revision.change_summary = payload.change_summary
|
||||
await _audit(
|
||||
db,
|
||||
item,
|
||||
"COLLABORATION_REVISION_NAMED",
|
||||
user,
|
||||
{"revision_id": str(revision.id), "revision_no": revision.revision_no},
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(revision)
|
||||
return revision
|
||||
@@ -1002,14 +929,6 @@ async def prepare_revision_preview(
|
||||
revision = await get_revision_or_404(db, item, revision_id)
|
||||
if not Path(revision.file_uri).is_file():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="协作修订内容不存在")
|
||||
await _audit(
|
||||
db,
|
||||
item,
|
||||
"COLLABORATION_REVISION_PREVIEWED",
|
||||
user,
|
||||
{"revision_id": str(revision.id), "revision_no": revision.revision_no},
|
||||
)
|
||||
await db.commit()
|
||||
return revision
|
||||
|
||||
|
||||
@@ -1040,7 +959,6 @@ async def copy_revision(
|
||||
content=content,
|
||||
source="COPY",
|
||||
user=user,
|
||||
source_file_id=item.id,
|
||||
)
|
||||
|
||||
|
||||
@@ -1063,7 +981,6 @@ async def restore_revision(
|
||||
locked = await db.get(CollaborationFile, item.id)
|
||||
if locked:
|
||||
locked.generation += 1
|
||||
await _audit(db, item, "COLLABORATION_REVISION_RESTORED", user, {"revision_no": revision.revision_no})
|
||||
await db.commit()
|
||||
await db.refresh(restored)
|
||||
return restored
|
||||
@@ -1111,7 +1028,6 @@ async def upsert_member(
|
||||
else:
|
||||
member = CollaborationMember(file_id=item.id, user_id=payload.user_id, role=payload.role, invited_by=user.id)
|
||||
db.add(member)
|
||||
await _audit(db, item, "COLLABORATION_MEMBER_UPSERTED", user, {"member_id": str(payload.user_id), "role": payload.role})
|
||||
await db.commit()
|
||||
await db.refresh(member)
|
||||
return CollaborationMemberRead(
|
||||
@@ -1132,7 +1048,6 @@ async def remove_member(db: AsyncSession, item: CollaborationFile, user_id: uuid
|
||||
if not member:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="协作成员不存在")
|
||||
await db.delete(member)
|
||||
await _audit(db, item, "COLLABORATION_MEMBER_REMOVED", user, {"member_id": str(user_id)})
|
||||
await db.commit()
|
||||
|
||||
|
||||
@@ -1192,7 +1107,6 @@ async def create_edit_request(
|
||||
request = CollaborationEditRequest(file_id=item.id, requester_id=user.id)
|
||||
db.add(request)
|
||||
await db.flush()
|
||||
await _audit(db, item, "COLLABORATION_EDIT_REQUESTED", user, {"request_id": str(request.id)})
|
||||
manager_ids = set((await db.scalars(
|
||||
select(StudyMember.user_id)
|
||||
.outerjoin(
|
||||
@@ -1286,16 +1200,30 @@ async def resolve_edit_request(
|
||||
request.status = payload.status
|
||||
request.resolved_by = user.id
|
||||
request.resolved_at = datetime.now(timezone.utc)
|
||||
approved = payload.status == "APPROVED"
|
||||
await notification_service.create_recipient_notifications(
|
||||
db,
|
||||
study_id=item.study_id,
|
||||
recipient_ids=[request.requester_id],
|
||||
category="COLLABORATION_EDIT_REQUEST_RESULT",
|
||||
priority="NORMAL",
|
||||
title="编辑权限申请已通过" if approved else "编辑权限申请未通过",
|
||||
message=f"您对“{item.title}”的编辑权限申请已{'通过' if approved else '被拒绝'}",
|
||||
action_path=(
|
||||
f"/knowledge/collaboration/{item.id}"
|
||||
if approved
|
||||
else "/knowledge/collaboration"
|
||||
),
|
||||
source_type="COLLABORATION_EDIT_REQUEST_RESULT",
|
||||
source_id=str(request.id),
|
||||
dedupe_key=f"collaboration-edit-request-result:{request.id}",
|
||||
requires_action=False,
|
||||
)
|
||||
await notification_service.resolve_source_notifications(
|
||||
db,
|
||||
source_type="COLLABORATION_EDIT_REQUEST",
|
||||
source_id=str(request.id),
|
||||
)
|
||||
await _audit(db, item, "COLLABORATION_EDIT_REQUEST_RESOLVED", user, {
|
||||
"request_id": str(request.id),
|
||||
"requester_id": str(request.requester_id),
|
||||
"status": payload.status,
|
||||
})
|
||||
await db.commit()
|
||||
await db.refresh(request)
|
||||
return _edit_request_read(request, requester)
|
||||
@@ -1341,10 +1269,6 @@ async def transfer_ownership(
|
||||
previous_owner_member.role = "MANAGER"
|
||||
locked.owner_id = payload.new_owner_id
|
||||
locked.updated_at = datetime.now(timezone.utc)
|
||||
await _audit(db, locked, "COLLABORATION_OWNERSHIP_TRANSFERRED", user, {
|
||||
"previous_owner_id": str(previous_owner_id),
|
||||
"new_owner_id": str(payload.new_owner_id),
|
||||
})
|
||||
await db.commit()
|
||||
await db.refresh(locked)
|
||||
return locked
|
||||
|
||||
Reference in New Issue
Block a user