test/custom-admin-ui #3
+274
-2
@@ -18,12 +18,15 @@ from core.utils import get_now, hash_password, is_password_hashed
|
|||||||
class FileService:
|
class FileService:
|
||||||
FILE_METADATA_KEY_PREFIX = "admin_file_metadata:"
|
FILE_METADATA_KEY_PREFIX = "admin_file_metadata:"
|
||||||
FILE_VIEW_PRESETS_KEY = "admin_file_view_presets"
|
FILE_VIEW_PRESETS_KEY = "admin_file_view_presets"
|
||||||
|
ADMIN_ACTIVITY_KEY = "admin_activity_events"
|
||||||
MAX_METADATA_NOTE_LENGTH = 2000
|
MAX_METADATA_NOTE_LENGTH = 2000
|
||||||
MAX_METADATA_TAGS = 12
|
MAX_METADATA_TAGS = 12
|
||||||
MAX_METADATA_TAG_LENGTH = 24
|
MAX_METADATA_TAG_LENGTH = 24
|
||||||
MAX_VIEW_PRESETS = 24
|
MAX_VIEW_PRESETS = 24
|
||||||
MAX_VIEW_PRESET_NAME_LENGTH = 32
|
MAX_VIEW_PRESET_NAME_LENGTH = 32
|
||||||
MAX_VIEW_PRESET_KEYWORD_LENGTH = 80
|
MAX_VIEW_PRESET_KEYWORD_LENGTH = 80
|
||||||
|
MAX_ADMIN_ACTIVITIES = 80
|
||||||
|
MAX_ADMIN_ACTIVITY_TEXT_LENGTH = 120
|
||||||
|
|
||||||
POLICY_ACTIONS = {
|
POLICY_ACTIONS = {
|
||||||
"extend_24h",
|
"extend_24h",
|
||||||
@@ -67,7 +70,13 @@ class FileService:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.file_storage: FileStorageInterface = storages[settings.file_storage]()
|
self._file_storage: Optional[FileStorageInterface] = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def file_storage(self) -> FileStorageInterface:
|
||||||
|
if self._file_storage is None:
|
||||||
|
self._file_storage = storages[settings.file_storage]()
|
||||||
|
return self._file_storage
|
||||||
|
|
||||||
def _file_metadata_key(self, file_id: int) -> str:
|
def _file_metadata_key(self, file_id: int) -> str:
|
||||||
return f"{self.FILE_METADATA_KEY_PREFIX}{file_id}"
|
return f"{self.FILE_METADATA_KEY_PREFIX}{file_id}"
|
||||||
@@ -80,7 +89,15 @@ class FileService:
|
|||||||
|
|
||||||
async def delete_file(self, file_id: int):
|
async def delete_file(self, file_id: int):
|
||||||
file_code = await FileCodes.get(id=file_id)
|
file_code = await FileCodes.get(id=file_id)
|
||||||
|
target_name = self._build_file_activity_name(file_code)
|
||||||
await self._delete_file_code(file_code)
|
await self._delete_file_code(file_code)
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="file.delete",
|
||||||
|
target_type="file",
|
||||||
|
target_id=file_id,
|
||||||
|
target_name=target_name,
|
||||||
|
count=1,
|
||||||
|
)
|
||||||
|
|
||||||
async def delete_files(self, file_ids: list[int]):
|
async def delete_files(self, file_ids: list[int]):
|
||||||
unique_ids = list(dict.fromkeys(file_ids))
|
unique_ids = list(dict.fromkeys(file_ids))
|
||||||
@@ -100,6 +117,20 @@ class FileService:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
failed.append({"id": file_id, "reason": str(exc)})
|
failed.append({"id": file_id, "reason": str(exc)})
|
||||||
|
|
||||||
|
if deleted:
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="files.batch_delete",
|
||||||
|
target_type="file",
|
||||||
|
count=len(deleted),
|
||||||
|
meta={
|
||||||
|
"requestedCount": len(file_ids),
|
||||||
|
"uniqueCount": len(unique_ids),
|
||||||
|
"deleted": deleted,
|
||||||
|
"missing": missing,
|
||||||
|
"failedCount": len(failed),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"requestedCount": len(file_ids),
|
"requestedCount": len(file_ids),
|
||||||
"requested_count": len(file_ids),
|
"requested_count": len(file_ids),
|
||||||
@@ -134,6 +165,21 @@ class FileService:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
failed.append({"id": file_id, "reason": str(exc)})
|
failed.append({"id": file_id, "reason": str(exc)})
|
||||||
|
|
||||||
|
if updated:
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="files.batch_update",
|
||||||
|
target_type="file",
|
||||||
|
count=len(updated),
|
||||||
|
meta={
|
||||||
|
"fields": sorted(update_data.keys()),
|
||||||
|
"requestedCount": len(file_ids),
|
||||||
|
"uniqueCount": len(unique_ids),
|
||||||
|
"updated": updated,
|
||||||
|
"missing": missing,
|
||||||
|
"failedCount": len(failed),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"requestedCount": len(file_ids),
|
"requestedCount": len(file_ids),
|
||||||
"requested_count": len(file_ids),
|
"requested_count": len(file_ids),
|
||||||
@@ -170,6 +216,14 @@ class FileService:
|
|||||||
)
|
)
|
||||||
|
|
||||||
await file_code.update_from_dict(update_data).save()
|
await file_code.update_from_dict(update_data).save()
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="file.policy_action",
|
||||||
|
target_type="file",
|
||||||
|
target_id=file_id,
|
||||||
|
target_name=self._build_file_activity_name(file_code),
|
||||||
|
count=1,
|
||||||
|
meta={"policyAction": action},
|
||||||
|
)
|
||||||
return await self.get_file_detail(file_id)
|
return await self.get_file_detail(file_id)
|
||||||
|
|
||||||
async def get_file_metadata(self, file_id: int) -> dict[str, Any]:
|
async def get_file_metadata(self, file_id: int) -> dict[str, Any]:
|
||||||
@@ -203,6 +257,18 @@ class FileService:
|
|||||||
key=self._file_metadata_key(file_id),
|
key=self._file_metadata_key(file_id),
|
||||||
defaults={"value": next_metadata},
|
defaults={"value": next_metadata},
|
||||||
)
|
)
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="file.metadata_update",
|
||||||
|
target_type="file",
|
||||||
|
target_id=file_id,
|
||||||
|
target_name=self._build_file_activity_name(file_code),
|
||||||
|
count=1,
|
||||||
|
meta={
|
||||||
|
"updateNote": update_note,
|
||||||
|
"updateTags": update_tags,
|
||||||
|
"tagCount": len(next_metadata["tags"]),
|
||||||
|
},
|
||||||
|
)
|
||||||
return await self.get_file_detail(file_id)
|
return await self.get_file_detail(file_id)
|
||||||
|
|
||||||
async def list_file_view_presets(self) -> dict[str, Any]:
|
async def list_file_view_presets(self) -> dict[str, Any]:
|
||||||
@@ -229,7 +295,8 @@ class FileService:
|
|||||||
(index for index, preset in enumerate(presets) if preset["id"] == preset_id),
|
(index for index, preset in enumerate(presets) if preset["id"] == preset_id),
|
||||||
-1,
|
-1,
|
||||||
)
|
)
|
||||||
if target_index >= 0:
|
is_update = target_index >= 0
|
||||||
|
if is_update:
|
||||||
preset = presets[target_index]
|
preset = presets[target_index]
|
||||||
next_preset = {
|
next_preset = {
|
||||||
**preset,
|
**preset,
|
||||||
@@ -256,6 +323,14 @@ class FileService:
|
|||||||
presets.append(next_preset)
|
presets.append(next_preset)
|
||||||
|
|
||||||
await self._save_file_view_presets(presets)
|
await self._save_file_view_presets(presets)
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="file.view_preset_update" if is_update else "file.view_preset_create",
|
||||||
|
target_type="view_preset",
|
||||||
|
target_id=next_preset["id"],
|
||||||
|
target_name=next_preset["name"],
|
||||||
|
count=1,
|
||||||
|
meta={"filters": normalized_filters},
|
||||||
|
)
|
||||||
return next_preset
|
return next_preset
|
||||||
|
|
||||||
async def delete_file_view_preset(self, preset_id: str) -> dict[str, Any]:
|
async def delete_file_view_preset(self, preset_id: str) -> dict[str, Any]:
|
||||||
@@ -264,11 +339,22 @@ class FileService:
|
|||||||
raise HTTPException(status_code=400, detail="请选择要删除的视图预设")
|
raise HTTPException(status_code=400, detail="请选择要删除的视图预设")
|
||||||
|
|
||||||
presets = await self._get_file_view_presets()
|
presets = await self._get_file_view_presets()
|
||||||
|
deleted_preset = next(
|
||||||
|
(preset for preset in presets if preset["id"] == preset_id),
|
||||||
|
None,
|
||||||
|
)
|
||||||
next_presets = [preset for preset in presets if preset["id"] != preset_id]
|
next_presets = [preset for preset in presets if preset["id"] != preset_id]
|
||||||
if len(next_presets) == len(presets):
|
if len(next_presets) == len(presets):
|
||||||
raise HTTPException(status_code=404, detail="视图预设不存在")
|
raise HTTPException(status_code=404, detail="视图预设不存在")
|
||||||
|
|
||||||
await self._save_file_view_presets(next_presets)
|
await self._save_file_view_presets(next_presets)
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="file.view_preset_delete",
|
||||||
|
target_type="view_preset",
|
||||||
|
target_id=preset_id,
|
||||||
|
target_name=(deleted_preset or {}).get("name", ""),
|
||||||
|
count=1,
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
"deleted": preset_id,
|
"deleted": preset_id,
|
||||||
"deletedPresetId": preset_id,
|
"deletedPresetId": preset_id,
|
||||||
@@ -315,6 +401,21 @@ class FileService:
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
failed.append({"id": file_id, "reason": str(exc)})
|
failed.append({"id": file_id, "reason": str(exc)})
|
||||||
|
|
||||||
|
if updated:
|
||||||
|
await self.record_admin_activity(
|
||||||
|
action="files.batch_policy_action",
|
||||||
|
target_type="file",
|
||||||
|
count=len(updated),
|
||||||
|
meta={
|
||||||
|
"policyAction": action,
|
||||||
|
"requestedCount": len(file_ids),
|
||||||
|
"uniqueCount": len(unique_ids),
|
||||||
|
"updated": updated,
|
||||||
|
"missing": missing,
|
||||||
|
"failedCount": len(failed),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"requestedCount": len(file_ids),
|
"requestedCount": len(file_ids),
|
||||||
"requested_count": len(file_ids),
|
"requested_count": len(file_ids),
|
||||||
@@ -631,6 +732,177 @@ class FileService:
|
|||||||
"updated_at": updated_at,
|
"updated_at": updated_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def list_admin_activities(self, limit: int = 8) -> dict[str, Any]:
|
||||||
|
limit = min(max(int(limit or 8), 1), self.MAX_ADMIN_ACTIVITIES)
|
||||||
|
activities = await self._get_admin_activities()
|
||||||
|
visible_activities = activities[:limit]
|
||||||
|
return {
|
||||||
|
"activities": visible_activities,
|
||||||
|
"items": visible_activities,
|
||||||
|
"total": len(activities),
|
||||||
|
}
|
||||||
|
|
||||||
|
async def record_admin_activity(
|
||||||
|
self,
|
||||||
|
action: str,
|
||||||
|
target_type: str,
|
||||||
|
target_id: Optional[Any] = None,
|
||||||
|
target_name: str = "",
|
||||||
|
count: int = 1,
|
||||||
|
meta: Optional[dict[str, Any]] = None,
|
||||||
|
) -> Optional[dict[str, Any]]:
|
||||||
|
try:
|
||||||
|
now = await get_now()
|
||||||
|
created_at = now.isoformat()
|
||||||
|
activity = self._normalize_admin_activity(
|
||||||
|
{
|
||||||
|
"id": self._build_admin_activity_id(
|
||||||
|
action=action,
|
||||||
|
target_type=target_type,
|
||||||
|
target_id=target_id,
|
||||||
|
target_name=target_name,
|
||||||
|
timestamp=now,
|
||||||
|
),
|
||||||
|
"action": action,
|
||||||
|
"targetType": target_type,
|
||||||
|
"target_type": target_type,
|
||||||
|
"targetId": target_id,
|
||||||
|
"target_id": target_id,
|
||||||
|
"targetName": target_name,
|
||||||
|
"target_name": target_name,
|
||||||
|
"count": count,
|
||||||
|
"meta": meta or {},
|
||||||
|
"createdAt": created_at,
|
||||||
|
"created_at": created_at,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if not activity:
|
||||||
|
return None
|
||||||
|
|
||||||
|
activities = await self._get_admin_activities()
|
||||||
|
next_activities = [
|
||||||
|
activity,
|
||||||
|
*[item for item in activities if item["id"] != activity["id"]],
|
||||||
|
][: self.MAX_ADMIN_ACTIVITIES]
|
||||||
|
await self._save_admin_activities(next_activities)
|
||||||
|
return activity
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def _get_admin_activities(self) -> list[dict[str, Any]]:
|
||||||
|
record = await KeyValue.filter(key=self.ADMIN_ACTIVITY_KEY).first()
|
||||||
|
raw_activities = record.value if record else []
|
||||||
|
if isinstance(raw_activities, dict):
|
||||||
|
raw_activities = (
|
||||||
|
raw_activities.get("activities") or raw_activities.get("items") or []
|
||||||
|
)
|
||||||
|
if not isinstance(raw_activities, list):
|
||||||
|
return []
|
||||||
|
|
||||||
|
activities = []
|
||||||
|
seen_ids = set()
|
||||||
|
for raw_activity in raw_activities:
|
||||||
|
activity = self._normalize_admin_activity(raw_activity)
|
||||||
|
if not activity or activity["id"] in seen_ids:
|
||||||
|
continue
|
||||||
|
seen_ids.add(activity["id"])
|
||||||
|
activities.append(activity)
|
||||||
|
if len(activities) >= self.MAX_ADMIN_ACTIVITIES:
|
||||||
|
break
|
||||||
|
|
||||||
|
activities.sort(key=lambda item: item.get("createdAt") or "", reverse=True)
|
||||||
|
return activities
|
||||||
|
|
||||||
|
async def _save_admin_activities(self, activities: list[dict[str, Any]]) -> None:
|
||||||
|
await KeyValue.update_or_create(
|
||||||
|
key=self.ADMIN_ACTIVITY_KEY,
|
||||||
|
defaults={"value": {"activities": activities}},
|
||||||
|
)
|
||||||
|
|
||||||
|
def _normalize_admin_activity(self, activity: Any) -> Optional[dict[str, Any]]:
|
||||||
|
if not isinstance(activity, dict):
|
||||||
|
return None
|
||||||
|
|
||||||
|
action = self._normalize_admin_activity_text(activity.get("action"))
|
||||||
|
target_type = self._normalize_admin_activity_text(
|
||||||
|
activity.get("targetType") or activity.get("target_type") or "system"
|
||||||
|
)
|
||||||
|
if not action:
|
||||||
|
return None
|
||||||
|
|
||||||
|
target_name = self._normalize_admin_activity_text(
|
||||||
|
activity.get("targetName") or activity.get("target_name")
|
||||||
|
)
|
||||||
|
created_at = activity.get("createdAt") or activity.get("created_at")
|
||||||
|
if isinstance(created_at, datetime):
|
||||||
|
created_at = created_at.isoformat()
|
||||||
|
created_at = str(created_at or "")
|
||||||
|
if not created_at:
|
||||||
|
return None
|
||||||
|
|
||||||
|
target_id = activity.get("targetId")
|
||||||
|
if target_id is None:
|
||||||
|
target_id = activity.get("target_id")
|
||||||
|
|
||||||
|
count = activity.get("count", 1)
|
||||||
|
try:
|
||||||
|
count = max(int(count), 1)
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
count = 1
|
||||||
|
|
||||||
|
meta = activity.get("meta")
|
||||||
|
if not isinstance(meta, dict):
|
||||||
|
meta = {}
|
||||||
|
|
||||||
|
activity_id = self._normalize_admin_activity_text(activity.get("id"))
|
||||||
|
if not activity_id:
|
||||||
|
activity_id = self._build_admin_activity_id(
|
||||||
|
action=action,
|
||||||
|
target_type=target_type,
|
||||||
|
target_id=target_id,
|
||||||
|
target_name=target_name,
|
||||||
|
timestamp=None,
|
||||||
|
seed=created_at,
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": activity_id,
|
||||||
|
"action": action,
|
||||||
|
"targetType": target_type,
|
||||||
|
"target_type": target_type,
|
||||||
|
"targetId": target_id,
|
||||||
|
"target_id": target_id,
|
||||||
|
"targetName": target_name,
|
||||||
|
"target_name": target_name,
|
||||||
|
"count": count,
|
||||||
|
"meta": meta,
|
||||||
|
"createdAt": created_at,
|
||||||
|
"created_at": created_at,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _normalize_admin_activity_text(self, value: Any) -> str:
|
||||||
|
return str(value or "").strip()[: self.MAX_ADMIN_ACTIVITY_TEXT_LENGTH]
|
||||||
|
|
||||||
|
def _build_admin_activity_id(
|
||||||
|
self,
|
||||||
|
action: str,
|
||||||
|
target_type: str,
|
||||||
|
target_id: Optional[Any],
|
||||||
|
target_name: str,
|
||||||
|
timestamp: Optional[datetime],
|
||||||
|
seed: Optional[str] = None,
|
||||||
|
) -> str:
|
||||||
|
timestamp_seed = (
|
||||||
|
str(int(timestamp.timestamp() * 1000)) if timestamp else str(seed or "activity")
|
||||||
|
)
|
||||||
|
digest = hashlib.sha1(
|
||||||
|
f"{timestamp_seed}:{action}:{target_type}:{target_id}:{target_name}".encode("utf-8")
|
||||||
|
).hexdigest()[:10]
|
||||||
|
return f"act_{timestamp_seed}_{digest}"
|
||||||
|
|
||||||
|
def _build_file_activity_name(self, file_code: FileCodes) -> str:
|
||||||
|
return (file_code.prefix + file_code.suffix) or file_code.code
|
||||||
|
|
||||||
async def _get_file_view_presets(self) -> list[dict[str, Any]]:
|
async def _get_file_view_presets(self) -> list[dict[str, Any]]:
|
||||||
record = await KeyValue.filter(key=self.FILE_VIEW_PRESETS_KEY).first()
|
record = await KeyValue.filter(key=self.FILE_VIEW_PRESETS_KEY).first()
|
||||||
raw_presets = record.value if record else []
|
raw_presets = record.value if record else []
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ async def dashboard(file_service: FileService = Depends(get_file_service)):
|
|||||||
else 0,
|
else 0,
|
||||||
reverse=True,
|
reverse=True,
|
||||||
)[:8]
|
)[:8]
|
||||||
|
recent_activities = await file_service.list_admin_activities(limit=8)
|
||||||
return APIResponse(
|
return APIResponse(
|
||||||
detail={
|
detail={
|
||||||
"totalFiles": len(all_codes),
|
"totalFiles": len(all_codes),
|
||||||
@@ -140,10 +141,21 @@ async def dashboard(file_service: FileService = Depends(get_file_service)):
|
|||||||
await build_dashboard_recent_file(file_code)
|
await build_dashboard_recent_file(file_code)
|
||||||
for file_code in recent_file_codes
|
for file_code in recent_file_codes
|
||||||
],
|
],
|
||||||
|
"recentActivities": recent_activities["activities"],
|
||||||
|
"recent_activities": recent_activities["activities"],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@admin_api.get("/activities")
|
||||||
|
async def admin_activities(
|
||||||
|
limit: int = 20,
|
||||||
|
file_service: FileService = Depends(get_file_service),
|
||||||
|
):
|
||||||
|
result = await file_service.list_admin_activities(limit=limit)
|
||||||
|
return APIResponse(detail=result)
|
||||||
|
|
||||||
|
|
||||||
@admin_api.delete("/file/delete")
|
@admin_api.delete("/file/delete")
|
||||||
async def file_delete(
|
async def file_delete(
|
||||||
data: IDData,
|
data: IDData,
|
||||||
@@ -454,9 +466,17 @@ async def get_config(
|
|||||||
async def update_config(
|
async def update_config(
|
||||||
data: dict,
|
data: dict,
|
||||||
config_service: ConfigService = Depends(get_config_service),
|
config_service: ConfigService = Depends(get_config_service),
|
||||||
|
file_service: FileService = Depends(get_file_service),
|
||||||
):
|
):
|
||||||
data.pop("themesChoices", None)
|
data.pop("themesChoices", None)
|
||||||
await config_service.update_config(data)
|
await config_service.update_config(data)
|
||||||
|
await file_service.record_admin_activity(
|
||||||
|
action="config.update",
|
||||||
|
target_type="config",
|
||||||
|
target_name="system",
|
||||||
|
count=1,
|
||||||
|
meta={"fields": sorted(data.keys())},
|
||||||
|
)
|
||||||
return APIResponse()
|
return APIResponse()
|
||||||
|
|
||||||
|
|
||||||
@@ -491,8 +511,16 @@ async def get_local_lists(
|
|||||||
async def delete_local_file(
|
async def delete_local_file(
|
||||||
item: DeleteItem,
|
item: DeleteItem,
|
||||||
local_file_service: LocalFileService = Depends(get_local_file_service),
|
local_file_service: LocalFileService = Depends(get_local_file_service),
|
||||||
|
file_service: FileService = Depends(get_file_service),
|
||||||
):
|
):
|
||||||
result = await local_file_service.delete_file(item.filename)
|
result = await local_file_service.delete_file(item.filename)
|
||||||
|
await file_service.record_admin_activity(
|
||||||
|
action="local_file.delete",
|
||||||
|
target_type="local_file",
|
||||||
|
target_name=item.filename,
|
||||||
|
count=1,
|
||||||
|
meta={"success": bool(result)},
|
||||||
|
)
|
||||||
return APIResponse(detail=result)
|
return APIResponse(detail=result)
|
||||||
|
|
||||||
|
|
||||||
@@ -502,16 +530,29 @@ async def share_local_file(
|
|||||||
file_service: FileService = Depends(get_file_service),
|
file_service: FileService = Depends(get_file_service),
|
||||||
):
|
):
|
||||||
share_info = await file_service.share_local_file(item)
|
share_info = await file_service.share_local_file(item)
|
||||||
|
await file_service.record_admin_activity(
|
||||||
|
action="local_file.share",
|
||||||
|
target_type="file",
|
||||||
|
target_id=share_info.get("id") if isinstance(share_info, dict) else None,
|
||||||
|
target_name=item.filename,
|
||||||
|
count=1,
|
||||||
|
meta={
|
||||||
|
"expireValue": item.expire_value,
|
||||||
|
"expireStyle": item.expire_style,
|
||||||
|
},
|
||||||
|
)
|
||||||
return APIResponse(detail=share_info)
|
return APIResponse(detail=share_info)
|
||||||
|
|
||||||
|
|
||||||
@admin_api.patch("/file/update")
|
@admin_api.patch("/file/update")
|
||||||
async def update_file(
|
async def update_file(
|
||||||
data: UpdateFileData,
|
data: UpdateFileData,
|
||||||
|
file_service: FileService = Depends(get_file_service),
|
||||||
):
|
):
|
||||||
file_code = await FileCodes.filter(id=data.id).first()
|
file_code = await FileCodes.filter(id=data.id).first()
|
||||||
if not file_code:
|
if not file_code:
|
||||||
raise HTTPException(status_code=404, detail="文件不存在")
|
raise HTTPException(status_code=404, detail="文件不存在")
|
||||||
|
target_name = file_service._build_file_activity_name(file_code)
|
||||||
update_data = {}
|
update_data = {}
|
||||||
|
|
||||||
if data.code is not None and data.code != file_code.code:
|
if data.code is not None and data.code != file_code.code:
|
||||||
@@ -533,4 +574,13 @@ async def update_file(
|
|||||||
update_data["expired_count"] = data.expired_count
|
update_data["expired_count"] = data.expired_count
|
||||||
|
|
||||||
await file_code.update_from_dict(update_data).save()
|
await file_code.update_from_dict(update_data).save()
|
||||||
|
if update_data:
|
||||||
|
await file_service.record_admin_activity(
|
||||||
|
action="file.update",
|
||||||
|
target_type="file",
|
||||||
|
target_id=data.id,
|
||||||
|
target_name=target_name,
|
||||||
|
count=1,
|
||||||
|
meta={"fields": sorted(update_data.keys())},
|
||||||
|
)
|
||||||
return APIResponse(detail="更新成功")
|
return APIResponse(detail="更新成功")
|
||||||
|
|||||||
Reference in New Issue
Block a user