144 lines
4.9 KiB
Python
144 lines
4.9 KiB
Python
from flask import Blueprint, jsonify, request, abort
|
||
from flask_login import login_required, current_user
|
||
|
||
from app.extensions import db
|
||
from app.models.folder import Folder
|
||
from app.utils.file_handler import delete_resource_file
|
||
|
||
folders_bp = Blueprint('folders', __name__)
|
||
|
||
|
||
# ── 递归删除辅助函数 ──────────────────────────────────────────────────────────
|
||
|
||
def _delete_folder_recursive(folder: Folder):
|
||
"""DFS 递归删除文件夹:先删子文件夹,再删资源(含磁盘文件),最后删自身"""
|
||
for child in list(folder.children):
|
||
_delete_folder_recursive(child)
|
||
for res in folder.resources.all():
|
||
delete_resource_file(res.file_path)
|
||
db.session.delete(res)
|
||
db.session.delete(folder)
|
||
|
||
|
||
# ── 路由 ──────────────────────────────────────────────────────────────────────
|
||
|
||
@folders_bp.route('/tree')
|
||
@login_required
|
||
def tree():
|
||
"""返回当前用户的完整文件夹树(JSON)"""
|
||
return jsonify(Folder.get_user_tree(current_user.id))
|
||
|
||
|
||
@folders_bp.route('/create', methods=['POST'])
|
||
@login_required
|
||
def create():
|
||
"""创建文件夹"""
|
||
name = request.form.get('name', '').strip()
|
||
parent_id_raw = request.form.get('parent_id', '').strip()
|
||
parent_id = int(parent_id_raw) if parent_id_raw.isdigit() else None
|
||
|
||
if not name or len(name) > 128:
|
||
return jsonify(ok=False, msg='文件夹名称无效(1-128位)'), 400
|
||
|
||
# 验证父文件夹归属
|
||
if parent_id is not None:
|
||
parent = db.session.get(Folder, parent_id)
|
||
if parent is None or parent.user_id != current_user.id:
|
||
abort(403)
|
||
|
||
# 同级重名校验(覆盖 NULL parent_id 场景)
|
||
dup = Folder.query.filter_by(
|
||
user_id=current_user.id,
|
||
parent_id=parent_id,
|
||
name=name
|
||
).first()
|
||
if dup:
|
||
return jsonify(ok=False, msg='该位置已存在同名文件夹'), 409
|
||
|
||
folder = Folder(user_id=current_user.id, name=name, parent_id=parent_id)
|
||
db.session.add(folder)
|
||
db.session.commit()
|
||
return jsonify(ok=True, folder={
|
||
'id': folder.id, 'name': folder.name,
|
||
'parent_id': folder.parent_id, 'resource_count': 0, 'children': []
|
||
})
|
||
|
||
|
||
@folders_bp.route('/<int:folder_id>/rename', methods=['POST'])
|
||
@login_required
|
||
def rename(folder_id):
|
||
"""重命名文件夹"""
|
||
folder = db.session.get(Folder, folder_id)
|
||
if folder is None or folder.user_id != current_user.id:
|
||
abort(403)
|
||
|
||
name = request.form.get('name', '').strip()
|
||
if not name or len(name) > 128:
|
||
return jsonify(ok=False, msg='文件夹名称无效'), 400
|
||
|
||
if name == folder.name:
|
||
return jsonify(ok=True)
|
||
|
||
# 同级重名校验
|
||
dup = Folder.query.filter_by(
|
||
user_id=current_user.id,
|
||
parent_id=folder.parent_id,
|
||
name=name
|
||
).filter(Folder.id != folder_id).first()
|
||
if dup:
|
||
return jsonify(ok=False, msg='该位置已存在同名文件夹'), 409
|
||
|
||
folder.name = name
|
||
db.session.commit()
|
||
return jsonify(ok=True)
|
||
|
||
|
||
@folders_bp.route('/<int:folder_id>/delete', methods=['POST'])
|
||
@login_required
|
||
def delete(folder_id):
|
||
"""递归删除文件夹(含所有子文件夹和资源)"""
|
||
folder = db.session.get(Folder, folder_id)
|
||
if folder is None or folder.user_id != current_user.id:
|
||
abort(403)
|
||
|
||
_delete_folder_recursive(folder)
|
||
db.session.commit()
|
||
return jsonify(ok=True)
|
||
|
||
|
||
@folders_bp.route('/<int:folder_id>/move', methods=['POST'])
|
||
@login_required
|
||
def move(folder_id):
|
||
"""移动文件夹到新的父文件夹"""
|
||
folder = db.session.get(Folder, folder_id)
|
||
if folder is None or folder.user_id != current_user.id:
|
||
abort(403)
|
||
|
||
parent_id_raw = request.form.get('parent_id', '').strip()
|
||
new_parent_id = int(parent_id_raw) if parent_id_raw.isdigit() else None
|
||
|
||
if new_parent_id is not None:
|
||
# 不能移动到自身
|
||
if new_parent_id == folder_id:
|
||
return jsonify(ok=False, msg='不能将文件夹移动到自身'), 400
|
||
# 不能移动到自己的子孙节点
|
||
if folder.is_ancestor_of(new_parent_id):
|
||
return jsonify(ok=False, msg='不能将文件夹移动到其子文件夹中'), 400
|
||
# 验证目标父文件夹归属
|
||
new_parent = db.session.get(Folder, new_parent_id)
|
||
if new_parent is None or new_parent.user_id != current_user.id:
|
||
abort(403)
|
||
|
||
# 同级重名校验
|
||
dup = Folder.query.filter_by(
|
||
user_id=current_user.id,
|
||
parent_id=new_parent_id,
|
||
name=folder.name
|
||
).filter(Folder.id != folder_id).first()
|
||
if dup:
|
||
return jsonify(ok=False, msg='目标位置已有同名文件夹'), 409
|
||
|
||
folder.parent_id = new_parent_id
|
||
db.session.commit()
|
||
return jsonify(ok=True)
|