from datetime import datetime from app.extensions import db class Folder(db.Model): __tablename__ = 'folders' __table_args__ = ( # 同一用户同一父目录下名称唯一 # 注意:MySQL 对 NULL 不强制唯一,根目录重名由路由层补充校验 db.UniqueConstraint('user_id', 'parent_id', 'name', name='uq_folder_user_parent_name'), ) id = db.Column(db.Integer, primary_key=True) user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'), nullable=False, index=True) name = db.Column(db.String(128), nullable=False) parent_id = db.Column(db.Integer, db.ForeignKey('folders.id', ondelete='CASCADE'), nullable=True, index=True) created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) # 自引用关系:父删 → 子自动删 children = db.relationship( 'Folder', backref=db.backref('parent', remote_side=[id]), lazy='select', cascade='all, delete-orphan' ) # 文件夹内的资源 resources = db.relationship( 'Resource', backref='folder', lazy='dynamic', foreign_keys='Resource.folder_id' ) # ── 业务方法 ───────────────────────────────────────────────── def is_ancestor_of(self, target_id: int) -> bool: """检测 self 是否是 target_id 的祖先(防止移动到自身子树产生循环引用)""" visited = set() current = Folder.query.get(target_id) while current is not None: if current.id == self.id: return True if current.id in visited: break visited.add(current.id) current = current.parent return False def get_breadcrumb(self) -> list: """从根到当前节点的路径列表(用于面包屑导航)""" path, node = [], self while node is not None: path.append(node) node = node.parent path.reverse() return path def to_dict(self) -> dict: return { 'id': self.id, 'name': self.name, 'parent_id': self.parent_id, 'resource_count': self.resources.count(), 'children': [c.to_dict() for c in sorted(self.children, key=lambda x: x.name)] } @staticmethod def get_user_tree(user_id: int) -> list: """返回某用户所有根文件夹的完整树(递归序列化)""" roots = (Folder.query .filter_by(user_id=user_id, parent_id=None) .order_by(Folder.name) .all()) return [f.to_dict() for f in roots] def __repr__(self): return f''