You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
195 lines
6.9 KiB
Python
195 lines
6.9 KiB
Python
from iti.applications.extensions import db
|
|
from iti.applications.common.crud import BaseModelMixin
|
|
from iti.applications.common.enums import StatusEnum, StorageTypeEnum
|
|
from iti.applications.common.utils import BaseSchema
|
|
from apiflask.fields import String, DateTime, Enum, Integer, Dict as DictField, Nested
|
|
from marshmallow import post_dump
|
|
|
|
|
|
class SysFile(BaseModelMixin):
|
|
"""文件记录表"""
|
|
|
|
__tablename__ = "sys_file"
|
|
|
|
filename = db.Column(db.String(255), nullable=False, comment="原始文件名")
|
|
file_key = db.Column(
|
|
db.String(512), nullable=False, unique=True, index=True, comment="存储路径"
|
|
)
|
|
file_hash = db.Column(db.String(64), nullable=True, index=True, comment="文件哈希")
|
|
|
|
mime_type = db.Column(db.String(128), nullable=True, comment="MIME类型")
|
|
file_size = db.Column(db.BigInteger, nullable=False, comment="文件大小(字节)")
|
|
extension = db.Column(db.String(32), nullable=True, comment="文件扩展名")
|
|
|
|
storage_type = db.Column(
|
|
db.Enum(StorageTypeEnum, values_callable=lambda x: [e.value for e in x]),
|
|
nullable=False,
|
|
default=StorageTypeEnum.LOCAL.value,
|
|
comment="存储类型",
|
|
)
|
|
storage_info = db.Column(db.JSON, nullable=True, comment="存储信息(bucket/region/endpoint/meta等)")
|
|
|
|
directory_id = db.Column(db.String(36), nullable=True, index=True, comment="所属目录ID")
|
|
|
|
metadata_ = db.Column("metadata", db.JSON, nullable=True, comment="扩展元数据")
|
|
|
|
status = db.Column(
|
|
db.Enum(StatusEnum, values_callable=lambda x: [e.value for e in x]),
|
|
nullable=False,
|
|
default=StatusEnum.ENABLED.value,
|
|
comment="状态",
|
|
)
|
|
|
|
# 关系
|
|
directory = db.relationship(
|
|
"SysFileDirectory",
|
|
primaryjoin="SysFile.directory_id == SysFileDirectory.id",
|
|
foreign_keys="SysFile.directory_id",
|
|
uselist=False,
|
|
lazy="noload",
|
|
)
|
|
|
|
|
|
class SysFileDirectory(BaseModelMixin):
|
|
"""文件目录"""
|
|
|
|
__tablename__ = "sys_file_directory"
|
|
|
|
name = db.Column(db.String(255), nullable=False, comment="目录名称")
|
|
path = db.Column(db.String(1024), nullable=False, index=True, comment="完整路径")
|
|
parent_id = db.Column(db.String(36), nullable=True, comment="父目录ID")
|
|
level = db.Column(db.Integer, default=0, comment="层级")
|
|
sort = db.Column(db.Integer, default=0, comment="排序")
|
|
icon = db.Column(db.String(128), nullable=True, comment="目录图标")
|
|
color = db.Column(db.String(32), nullable=True, comment="颜色标记")
|
|
description = db.Column(db.Text, nullable=True, comment="目录描述")
|
|
default_storage_type = db.Column(
|
|
db.String(32), nullable=True, comment="默认存储类型"
|
|
)
|
|
status = db.Column(
|
|
db.Enum(StatusEnum, values_callable=lambda x: [e.value for e in x]),
|
|
nullable=False,
|
|
default=StatusEnum.ENABLED.value,
|
|
comment="状态",
|
|
)
|
|
|
|
# 关系
|
|
parent = db.relationship(
|
|
"SysFileDirectory",
|
|
primaryjoin="SysFileDirectory.parent_id == SysFileDirectory.id",
|
|
foreign_keys="SysFileDirectory.parent_id",
|
|
uselist=False,
|
|
remote_side="SysFileDirectory.id",
|
|
lazy="noload",
|
|
)
|
|
children = db.relationship(
|
|
"SysFileDirectory",
|
|
primaryjoin="SysFileDirectory.id == SysFileDirectory.parent_id",
|
|
foreign_keys="SysFileDirectory.parent_id",
|
|
uselist=True,
|
|
lazy="noload",
|
|
viewonly=True,
|
|
)
|
|
files = db.relationship(
|
|
"SysFile",
|
|
primaryjoin="SysFileDirectory.id == SysFile.directory_id",
|
|
foreign_keys="SysFile.directory_id",
|
|
uselist=True,
|
|
lazy="noload",
|
|
viewonly=True,
|
|
)
|
|
|
|
|
|
class SysFileSchema(BaseSchema):
|
|
id = String()
|
|
filename = String()
|
|
file_key = String()
|
|
file_hash = String()
|
|
mime_type = String()
|
|
file_size = Integer()
|
|
extension = String()
|
|
storage_type = Enum(StorageTypeEnum, by_value=True)
|
|
storage_info = DictField()
|
|
directory_id = String()
|
|
metadata_ = DictField(data_key="metadata")
|
|
status = Enum(StatusEnum, by_value=True)
|
|
created_at = DateTime(format="%Y-%m-%d %H:%M:%S")
|
|
updated_at = DateTime(format="%Y-%m-%d %H:%M:%S")
|
|
|
|
url = String(dump_only=True)
|
|
preview_url = String(dump_only=True)
|
|
thumbnail_url = String(dump_only=True)
|
|
file_category = String(dump_only=True)
|
|
file_size_text = String(dump_only=True)
|
|
|
|
@post_dump
|
|
def flatten_metadata(self, data, **kwargs):
|
|
metadata = data.get("metadata") or {}
|
|
for key, value in metadata.items():
|
|
data.setdefault(key, value)
|
|
|
|
from ..service.sys_file import SysFileService
|
|
|
|
file_id = data.get("id")
|
|
if file_id:
|
|
try:
|
|
data["url"] = SysFileService.get_file_url(file_id)
|
|
data["previewUrl"] = SysFileService.get_preview_url(file_id)
|
|
data["thumbnailUrl"] = SysFileService.get_thumbnail_url(file_id)
|
|
except Exception:
|
|
data["url"] = None
|
|
data["previewUrl"] = None
|
|
data["thumbnailUrl"] = None
|
|
|
|
# 文件分类
|
|
mime_type = data.get("mimeType", "") or data.get("mime_type", "") or ""
|
|
if mime_type.startswith("image/"):
|
|
data["fileCategory"] = "image"
|
|
elif mime_type.startswith("video/"):
|
|
data["fileCategory"] = "video"
|
|
elif mime_type.startswith("audio/"):
|
|
data["fileCategory"] = "audio"
|
|
elif "pdf" in mime_type or "word" in mime_type or "document" in mime_type:
|
|
data["fileCategory"] = "document"
|
|
elif "zip" in mime_type or "rar" in mime_type or "7z" in mime_type:
|
|
data["fileCategory"] = "archive"
|
|
else:
|
|
data["fileCategory"] = "other"
|
|
|
|
# 文件大小文本
|
|
file_size = data.get("fileSize") or data.get("file_size") or 0
|
|
try:
|
|
file_size = int(file_size)
|
|
except (TypeError, ValueError):
|
|
file_size = 0
|
|
|
|
if file_size < 1024:
|
|
data["fileSizeText"] = f"{file_size} B"
|
|
elif file_size < 1024 * 1024:
|
|
data["fileSizeText"] = f"{file_size / 1024:.2f} KB"
|
|
elif file_size < 1024 * 1024 * 1024:
|
|
data["fileSizeText"] = f"{file_size / (1024 * 1024):.2f} MB"
|
|
else:
|
|
data["fileSizeText"] = f"{file_size / (1024 * 1024 * 1024):.2f} GB"
|
|
|
|
return data
|
|
|
|
|
|
class SysFileDirectorySchema(BaseSchema):
|
|
id = String()
|
|
name = String()
|
|
path = String()
|
|
parent_id = String()
|
|
level = Integer()
|
|
sort = Integer()
|
|
icon = String()
|
|
color = String()
|
|
description = String()
|
|
default_storage_type = String()
|
|
status = Enum(StatusEnum, by_value=True)
|
|
created_at = DateTime(format="%Y-%m-%d %H:%M:%S")
|
|
updated_at = DateTime(format="%Y-%m-%d %H:%M:%S")
|
|
children = Nested(
|
|
"SysFileDirectorySchema", many=True, dump_only=True, exclude=["children"]
|
|
)
|