2025-09-12 16:15:13 +08:00
|
|
|
#!/usr/bin/env python
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
脚本文件管理服务
|
|
|
|
提供脚本文件的CRUD操作和文件系统管理
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
2025-09-25 10:52:52 +08:00
|
|
|
import zipfile
|
|
|
|
import json
|
2025-09-12 16:15:13 +08:00
|
|
|
from datetime import datetime
|
|
|
|
from pathlib import Path
|
2025-09-25 10:52:52 +08:00
|
|
|
from typing import Dict, Any
|
2025-09-20 16:50:45 +08:00
|
|
|
from sqlalchemy import and_, or_, select
|
2025-09-25 10:52:52 +08:00
|
|
|
from io import BytesIO
|
2025-09-12 16:15:13 +08:00
|
|
|
|
2025-09-25 10:52:52 +08:00
|
|
|
from data.session import get_async_session
|
2025-09-12 16:15:13 +08:00
|
|
|
from data.models.script_project import VWEDScriptProject
|
|
|
|
from data.models.script_file import VWEDScriptFile
|
|
|
|
from utils.logger import get_logger
|
2025-09-20 16:50:45 +08:00
|
|
|
from config.settings import settings
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
logger = get_logger("services.script_file")
|
|
|
|
|
|
|
|
|
|
|
|
class ScriptFileService:
|
|
|
|
"""脚本文件管理服务"""
|
|
|
|
|
|
|
|
def __init__(self):
|
2025-09-20 16:50:45 +08:00
|
|
|
self.base_path = Path(settings.SCRIPT_SAVE_PATH)
|
2025-09-12 16:15:13 +08:00
|
|
|
self.base_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
async def create_project(self, project_name: str, description: str = "",
|
|
|
|
created_by: str = None) -> Dict[str, Any]:
|
|
|
|
"""创建新项目"""
|
|
|
|
try:
|
|
|
|
# 创建项目路径
|
|
|
|
project_path = f"projects/{project_name}"
|
|
|
|
full_project_path = self.base_path / project_path
|
|
|
|
|
|
|
|
# 检查项目是否已存在
|
|
|
|
async with get_async_session() as session:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = select(VWEDScriptProject).where(
|
2025-09-12 16:15:13 +08:00
|
|
|
VWEDScriptProject.project_name == project_name
|
2025-09-20 16:50:45 +08:00
|
|
|
)
|
|
|
|
result = await session.execute(query)
|
|
|
|
existing_project = result.scalar_one_or_none()
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
if existing_project:
|
|
|
|
return {"success": False, "error": f"项目 {project_name} 已存在"}
|
|
|
|
|
|
|
|
# 创建文件系统目录
|
|
|
|
full_project_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
# 创建数据库记录
|
|
|
|
new_project = VWEDScriptProject(
|
|
|
|
project_name=project_name,
|
|
|
|
project_path=project_path,
|
|
|
|
description=description,
|
|
|
|
created_by=created_by
|
|
|
|
)
|
|
|
|
session.add(new_project)
|
|
|
|
await session.commit()
|
|
|
|
await session.refresh(new_project)
|
|
|
|
|
|
|
|
logger.info(f"项目创建成功: {project_name}")
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"project": new_project.to_dict(),
|
|
|
|
"message": f"项目 {project_name} 创建成功"
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"创建项目失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"创建项目失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def get_projects(self, status: str = "active") -> Dict[str, Any]:
|
|
|
|
"""获取项目列表"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = select(VWEDScriptProject)
|
2025-09-12 16:15:13 +08:00
|
|
|
if status:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = query.where(VWEDScriptProject.status == status)
|
2025-09-12 16:15:13 +08:00
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
result = await session.execute(query)
|
|
|
|
projects = result.scalars().all()
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"projects": [project.to_dict() for project in projects]
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"获取项目列表失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"获取项目列表失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def create_file(self, project_id: int, file_name: str, file_path: str,
|
|
|
|
content: str = "", file_type: str = "python",
|
|
|
|
created_by: str = None) -> Dict[str, Any]:
|
|
|
|
"""创建脚本文件"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
# 检查项目是否存在
|
|
|
|
project = await session.get(VWEDScriptProject, project_id)
|
|
|
|
if not project:
|
|
|
|
return {"success": False, "error": f"项目ID {project_id} 不存在"}
|
|
|
|
|
|
|
|
# 构建完整文件路径
|
|
|
|
full_file_path = self.base_path / project.project_path / file_path
|
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
# 检查文件名是否已存在(同一项目下不能有重名文件)
|
|
|
|
name_query = select(VWEDScriptFile).where(
|
|
|
|
and_(VWEDScriptFile.project_id == project_id,
|
2025-09-25 10:52:52 +08:00
|
|
|
VWEDScriptFile.file_name == file_name,
|
|
|
|
VWEDScriptFile.is_deleted==False)
|
2025-09-20 16:50:45 +08:00
|
|
|
)
|
|
|
|
name_result = await session.execute(name_query)
|
|
|
|
existing_names = name_result.scalars().all()
|
|
|
|
|
|
|
|
if existing_names:
|
|
|
|
return {"success": False, "error": f"同一项目下已存在名为 '{file_name}' 的文件"}
|
|
|
|
|
|
|
|
# 检查文件路径是否已存在
|
|
|
|
path_query = select(VWEDScriptFile).where(
|
2025-09-12 16:15:13 +08:00
|
|
|
and_(VWEDScriptFile.project_id == project_id,
|
2025-09-25 10:52:52 +08:00
|
|
|
VWEDScriptFile.file_path == file_path,
|
|
|
|
VWEDScriptFile.is_deleted==False
|
|
|
|
)
|
2025-09-20 16:50:45 +08:00
|
|
|
)
|
|
|
|
path_result = await session.execute(path_query)
|
|
|
|
existing_paths = path_result.scalars().all()
|
2025-09-12 16:15:13 +08:00
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
if existing_paths:
|
|
|
|
return {"success": False, "error": f"文件路径 '{file_path}' 已存在"}
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# 创建目录
|
|
|
|
full_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
|
|
# 写入文件内容
|
|
|
|
with open(full_file_path, 'w', encoding='utf-8') as f:
|
|
|
|
f.write(content)
|
|
|
|
|
|
|
|
# 检测boot函数
|
|
|
|
has_boot_function = self._check_boot_function(content)
|
|
|
|
|
|
|
|
# 计算文件大小
|
|
|
|
file_size = full_file_path.stat().st_size
|
|
|
|
|
|
|
|
# 创建数据库记录
|
|
|
|
new_file = VWEDScriptFile(
|
|
|
|
project_id=project_id,
|
|
|
|
file_name=file_name,
|
2025-09-25 10:52:52 +08:00
|
|
|
file_path=file_path,
|
2025-09-12 16:15:13 +08:00
|
|
|
file_type=file_type,
|
|
|
|
content=content,
|
|
|
|
size=file_size,
|
|
|
|
has_boot_function=has_boot_function,
|
|
|
|
created_by=created_by
|
|
|
|
)
|
|
|
|
session.add(new_file)
|
|
|
|
await session.commit()
|
|
|
|
await session.refresh(new_file)
|
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
logger.info(f"文件创建成功: {full_file_path}")
|
2025-09-12 16:15:13 +08:00
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"file": new_file.to_dict(),
|
|
|
|
"message": f"文件 {file_name} 创建成功"
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"创建文件失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"创建文件失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def update_file_content(self, file_id: int, content: str,
|
|
|
|
updated_by: str = None) -> Dict[str, Any]:
|
|
|
|
"""更新文件内容"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
script_file = await session.get(VWEDScriptFile, file_id)
|
2025-09-25 10:52:52 +08:00
|
|
|
if not script_file or script_file.is_deleted:
|
|
|
|
return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# 获取项目信息
|
|
|
|
project = await session.get(VWEDScriptProject, script_file.project_id)
|
|
|
|
|
2025-09-25 10:52:52 +08:00
|
|
|
# 处理文件路径,如果是绝对路径则直接使用,否则相对于基础路径
|
|
|
|
# full_file_path = Path(script_file.file_path)
|
|
|
|
if Path(script_file.file_path).is_absolute():
|
|
|
|
full_file_path = Path(script_file.file_path)
|
|
|
|
else:
|
|
|
|
full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
|
|
|
|
|
|
|
|
|
2025-09-12 16:15:13 +08:00
|
|
|
# 写入新内容
|
|
|
|
with open(full_file_path, 'w', encoding='utf-8') as f:
|
|
|
|
f.write(content)
|
|
|
|
|
|
|
|
# 更新数据库记录
|
|
|
|
script_file.content = content
|
|
|
|
script_file.size = full_file_path.stat().st_size
|
|
|
|
script_file.has_boot_function = self._check_boot_function(content)
|
|
|
|
script_file.updated_at = datetime.now()
|
2025-09-25 10:52:52 +08:00
|
|
|
if updated_by:
|
|
|
|
script_file.updated_by = updated_by
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
logger.info(f"文件更新成功: {project.project_name}/{script_file.file_path}")
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"file": script_file.to_dict(),
|
|
|
|
"message": "文件内容更新成功"
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"更新文件内容失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"更新文件内容失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def get_file_content(self, file_id: int) -> Dict[str, Any]:
|
|
|
|
"""获取文件内容"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
script_file = await session.get(VWEDScriptFile, file_id)
|
2025-09-25 10:52:52 +08:00
|
|
|
if not script_file or script_file.is_deleted:
|
|
|
|
return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
|
|
|
|
|
|
|
|
# # 从数据库读取内容(优先)
|
|
|
|
# if script_file.content:
|
|
|
|
# return {
|
|
|
|
# "success": True,
|
|
|
|
# "file": script_file.to_dict(),
|
|
|
|
# "content": script_file.content
|
|
|
|
# }
|
|
|
|
|
|
|
|
# 获取项目信息
|
2025-09-12 16:15:13 +08:00
|
|
|
project = await session.get(VWEDScriptProject, script_file.project_id)
|
|
|
|
|
2025-09-25 10:52:52 +08:00
|
|
|
# 处理文件路径,如果是绝对路径则直接使用,否则相对于基础路径
|
|
|
|
if Path(script_file.file_path).is_absolute():
|
|
|
|
full_file_path = Path(script_file.file_path)
|
|
|
|
else:
|
|
|
|
full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
|
2025-09-12 16:15:13 +08:00
|
|
|
if not full_file_path.exists():
|
2025-09-25 10:52:52 +08:00
|
|
|
return {"success": False, "error": f"物理文件不存在: {full_file_path}"}
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
with open(full_file_path, 'r', encoding='utf-8') as f:
|
|
|
|
content = f.read()
|
|
|
|
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"file": script_file.to_dict(),
|
|
|
|
"content": content
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"获取文件内容失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"获取文件内容失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def get_project_files(self, project_id: int, include_content: bool = False) -> Dict[str, Any]:
|
|
|
|
"""获取项目的所有文件"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
project = await session.get(VWEDScriptProject, project_id)
|
|
|
|
if not project:
|
|
|
|
return {"success": False, "error": f"项目ID {project_id} 不存在"}
|
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
query = select(VWEDScriptFile).where(
|
2025-09-25 10:52:52 +08:00
|
|
|
and_(
|
|
|
|
VWEDScriptFile.project_id == project_id,
|
|
|
|
VWEDScriptFile.is_deleted == False
|
|
|
|
)
|
2025-09-20 16:50:45 +08:00
|
|
|
)
|
|
|
|
result = await session.execute(query)
|
|
|
|
files = result.scalars().all()
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
file_list = []
|
|
|
|
for file in files:
|
|
|
|
file_dict = file.to_dict()
|
|
|
|
if include_content and file.content:
|
|
|
|
file_dict['content'] = file.content
|
|
|
|
file_list.append(file_dict)
|
|
|
|
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"project": project.to_dict(),
|
|
|
|
"files": file_list
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"获取项目文件列表失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"获取项目文件列表失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def delete_file(self, file_id: int) -> Dict[str, Any]:
|
|
|
|
"""删除文件"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
script_file = await session.get(VWEDScriptFile, file_id)
|
2025-09-25 10:52:52 +08:00
|
|
|
if not script_file or script_file.is_deleted:
|
|
|
|
return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
project = await session.get(VWEDScriptProject, script_file.project_id)
|
2025-09-25 10:52:52 +08:00
|
|
|
|
|
|
|
# 构建完整文件路径
|
|
|
|
if Path(script_file.file_path).is_absolute():
|
|
|
|
full_file_path = Path(script_file.file_path)
|
|
|
|
else:
|
|
|
|
full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# 删除物理文件
|
2025-09-25 10:52:52 +08:00
|
|
|
try:
|
|
|
|
if full_file_path.exists():
|
|
|
|
full_file_path.unlink()
|
|
|
|
logger.info(f"物理文件已删除: {full_file_path}")
|
|
|
|
except Exception as file_error:
|
|
|
|
logger.warning(f"删除物理文件失败: {file_error}")
|
|
|
|
|
|
|
|
# 软删除:标记为已删除
|
|
|
|
script_file.is_deleted = True
|
|
|
|
script_file.status = 'deleted'
|
|
|
|
script_file.updated_at = datetime.now()
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
logger.info(f"文件删除成功: {project.project_name}/{script_file.file_path}")
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"message": f"文件 {script_file.file_name} 删除成功"
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"删除文件失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"删除文件失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def search_files(self, keyword: str, project_id: int = None,
|
|
|
|
file_type: str = None, has_boot: bool = None) -> Dict[str, Any]:
|
|
|
|
"""搜索文件"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
2025-09-25 10:52:52 +08:00
|
|
|
# 基础查询:排除已删除的文件
|
|
|
|
query = select(VWEDScriptFile).where(VWEDScriptFile.is_deleted == False)
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# 关键字搜索(文件名或内容)
|
|
|
|
if keyword:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = query.where(
|
2025-09-12 16:15:13 +08:00
|
|
|
or_(VWEDScriptFile.file_name.contains(keyword),
|
|
|
|
VWEDScriptFile.content.contains(keyword))
|
|
|
|
)
|
|
|
|
|
|
|
|
# 项目筛选
|
|
|
|
if project_id:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = query.where(VWEDScriptFile.project_id == project_id)
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# 文件类型筛选
|
|
|
|
if file_type:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = query.where(VWEDScriptFile.file_type == file_type)
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# boot函数筛选
|
|
|
|
if has_boot is not None:
|
2025-09-20 16:50:45 +08:00
|
|
|
query = query.where(VWEDScriptFile.has_boot_function == has_boot)
|
2025-09-12 16:15:13 +08:00
|
|
|
|
2025-09-20 16:50:45 +08:00
|
|
|
result = await session.execute(query)
|
|
|
|
files = result.scalars().all()
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
# 加载项目信息
|
|
|
|
file_list = []
|
|
|
|
for file in files:
|
|
|
|
project = await session.get(VWEDScriptProject, file.project_id)
|
|
|
|
file_dict = file.to_dict()
|
|
|
|
file_dict['project_name'] = project.project_name
|
|
|
|
file_list.append(file_dict)
|
|
|
|
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"files": file_list,
|
|
|
|
"count": len(file_list)
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"搜索文件失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"搜索文件失败: {str(e)}"}
|
|
|
|
|
|
|
|
def _check_boot_function(self, content: str) -> bool:
|
|
|
|
"""检查是否包含boot函数"""
|
|
|
|
try:
|
|
|
|
# 简单检查是否定义了boot函数
|
|
|
|
return "def boot(" in content or "def boot():" in content
|
|
|
|
except:
|
|
|
|
return False
|
|
|
|
|
|
|
|
async def validate_script_syntax(self, content: str) -> Dict[str, Any]:
|
|
|
|
"""验证脚本语法"""
|
|
|
|
try:
|
|
|
|
compile(content, '<script>', 'exec')
|
|
|
|
return {"success": True, "valid": True, "message": "语法检查通过"}
|
|
|
|
except SyntaxError as e:
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"valid": False,
|
|
|
|
"error": f"语法错误: {e.msg}",
|
|
|
|
"line": e.lineno,
|
|
|
|
"offset": e.offset
|
|
|
|
}
|
|
|
|
except Exception as e:
|
|
|
|
return {"success": False, "error": f"语法检查失败: {str(e)}"}
|
2025-09-25 10:52:52 +08:00
|
|
|
|
|
|
|
async def delete_project(self, project_id: int) -> Dict[str, Any]:
|
|
|
|
"""删除项目(逻辑删除)"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
project = await session.get(VWEDScriptProject, project_id)
|
|
|
|
if not project or project.status == 'deleted':
|
|
|
|
return {"success": False, "error": f"项目ID {project_id} 不存在或已被删除"}
|
|
|
|
|
|
|
|
# 逻辑删除:标记为已删除
|
|
|
|
project.status = 'deleted'
|
|
|
|
project.updated_at = datetime.now()
|
|
|
|
|
|
|
|
# 同时逻辑删除项目下的所有文件
|
|
|
|
query = select(VWEDScriptFile).where(
|
|
|
|
and_(
|
|
|
|
VWEDScriptFile.project_id == project_id,
|
|
|
|
VWEDScriptFile.is_deleted == False
|
|
|
|
)
|
|
|
|
)
|
|
|
|
result = await session.execute(query)
|
|
|
|
files = result.scalars().all()
|
|
|
|
|
|
|
|
for file in files:
|
|
|
|
file.is_deleted = True
|
|
|
|
file.status = 'deleted'
|
|
|
|
file.updated_at = datetime.now()
|
|
|
|
|
|
|
|
await session.commit()
|
|
|
|
|
|
|
|
logger.info(f"项目删除成功: {project.project_name}")
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"message": f"项目 {project.project_name} 删除成功"
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"删除项目失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"删除项目失败: {str(e)}"}
|
|
|
|
|
|
|
|
async def export_file(self, file_id: int) -> Dict[str, Any]:
|
|
|
|
"""导出单个脚本文件"""
|
|
|
|
try:
|
|
|
|
async with get_async_session() as session:
|
|
|
|
script_file = await session.get(VWEDScriptFile, file_id)
|
|
|
|
if not script_file or script_file.is_deleted:
|
|
|
|
return {"success": False, "error": f"文件ID {file_id} 不存在或已被删除"}
|
|
|
|
|
|
|
|
# 获取项目信息
|
|
|
|
project = await session.get(VWEDScriptProject, script_file.project_id)
|
|
|
|
|
|
|
|
# 获取文件内容
|
|
|
|
if script_file.content:
|
|
|
|
content = script_file.content
|
|
|
|
else:
|
|
|
|
# 从文件系统读取
|
|
|
|
if Path(script_file.file_path).is_absolute():
|
|
|
|
full_file_path = Path(script_file.file_path)
|
|
|
|
else:
|
|
|
|
full_file_path = Path(os.path.join(self.base_path, project.project_path, script_file.file_path))
|
|
|
|
|
|
|
|
if full_file_path.exists():
|
|
|
|
with open(full_file_path, 'r', encoding='utf-8') as f:
|
|
|
|
content = f.read()
|
|
|
|
else:
|
|
|
|
return {"success": False, "error": f"物理文件不存在: {full_file_path}"}
|
|
|
|
|
|
|
|
logger.info(f"文件导出成功: {script_file.file_name}")
|
|
|
|
return {
|
|
|
|
"success": True,
|
|
|
|
"file_name": script_file.file_name,
|
|
|
|
"content": content,
|
|
|
|
"file_info": {
|
|
|
|
"id": script_file.id,
|
|
|
|
"file_name": script_file.file_name,
|
|
|
|
"file_path": script_file.file_path,
|
|
|
|
"file_type": script_file.file_type,
|
|
|
|
"project_name": project.project_name,
|
|
|
|
"size": len(content.encode('utf-8')),
|
|
|
|
"created_at": script_file.created_at.isoformat() if script_file.created_at else None,
|
|
|
|
"updated_at": script_file.updated_at.isoformat() if script_file.updated_at else None
|
|
|
|
},
|
|
|
|
"message": f"文件 {script_file.file_name} 导出成功"
|
|
|
|
}
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
logger.error(f"导出文件失败: {e}", exc_info=True)
|
|
|
|
return {"success": False, "error": f"导出文件失败: {str(e)}"}
|
2025-09-12 16:15:13 +08:00
|
|
|
|
|
|
|
|
|
|
|
# 全局文件服务实例
|
|
|
|
_file_service = ScriptFileService()
|
|
|
|
|
|
|
|
|
|
|
|
def get_file_service() -> ScriptFileService:
|
|
|
|
"""获取文件服务实例"""
|
|
|
|
return _file_service
|