feat(workspace): 新增 get_space_dir/get_project_dir/get_project_apps_modules/resolve_workspace_path 并注册到 dspy 环境,支持工作空间弹窗显示项目目录+apps/modules 引用

This commit is contained in:
ymq 2026-08-24 17:56:47 +08:00
parent 2659284e05
commit b0273a4f9c

View File

@ -155,6 +155,92 @@ async def get_workspace_dir(sor, uid, session_id=''):
return build_workspace_path(workspace_base, org_id, space, pname, pid), workspace_base
async def get_space_dir(sor, uid, session_id=''):
"""返回机构工作空间层 {space}/projects/apps/modules 三目录所在)。
新结构项目目录迁到 {space}/projects/{项目名}/ apps/modules/ 平级
workspace_dir 仍是旧结构 {space}/{项目名}space = 其父目录
"""
ws_dir, workspace_base = await get_workspace_dir(sor, uid, session_id)
if not ws_dir:
return '', workspace_base
return os.path.dirname(ws_dir.rstrip('/')), workspace_base
async def get_project_dir(sor, uid, session_id=''):
"""返回项目目录 {space}/projects/{项目名}/新结构docs/env/spec.json/deliverables 所在)。"""
space_dir, workspace_base = await get_space_dir(sor, uid, session_id)
if not space_dir:
return '', workspace_base
pid = await get_session_project_id(sor, uid, session_id)
dname = ''
if pid:
try:
r = await sor.sqlExe(
"SELECT directory_name, name FROM sd_projects WHERE id=${p}$ LIMIT 1",
{"p": pid})
if r:
dname = getattr(r[0], 'directory_name', '') or getattr(r[0], 'name', '') or ''
except Exception:
pass
if not dname:
ws_dir, _ = await get_workspace_dir(sor, uid, session_id)
dname = os.path.basename(ws_dir.rstrip('/'))
return os.path.join(space_dir, 'projects', dname), workspace_base
async def get_project_apps_modules(sor, uid, session_id=''):
"""读项目目录下 {应用名}_spec.json返回 (apps, modules)。
apps = spec.json app 字段modules = referenced_modules + generated_modules 去重
"""
import json as _json
project_dir, _ = await get_project_dir(sor, uid, session_id)
if not project_dir or not os.path.isdir(project_dir):
return [], []
apps = []
modules = set()
try:
for f in sorted(os.listdir(project_dir)):
if not f.endswith('_spec.json'):
continue
fp = os.path.join(project_dir, f)
try:
with open(fp, 'r', encoding='utf-8') as fh:
data = _json.loads(fh.read())
except Exception:
continue
if not isinstance(data, dict):
continue
app = (data.get('app') or '').strip()
if app:
apps.append(app)
for m in (data.get('referenced_modules') or []) + (data.get('generated_modules') or []):
if isinstance(m, str) and m.strip():
modules.add(m.strip())
except Exception:
pass
return sorted(set(apps)), sorted(modules)
def resolve_workspace_path(project_dir, space_dir, node_id):
"""把工作空间节点 ID 解析成绝对路径。
- '@apps/xxx' space_dir/apps/xxx项目用到的应用
- '@modules/xxx' space_dir/modules/xxx项目用到的模块
- '__root__' / project_dir项目目录根
- 其他 project_dir/xxx项目目录内的相对路径
"""
node_id = (node_id or '').strip()
if node_id.startswith('@apps/'):
return os.path.join(space_dir, 'apps', node_id[len('@apps/'):])
if node_id.startswith('@modules/'):
return os.path.join(space_dir, 'modules', node_id[len('@modules/'):])
if not node_id or node_id == '__root__':
return project_dir
return os.path.join(project_dir, node_id)
async def get_workspace_path(sor, user_id, session_id=''):
"""获取用户当前项目的工作空间路径(无项目时返回 None"""
ws, _ = await get_workspace_dir(sor, user_id, session_id)
@ -314,7 +400,12 @@ def load_workspace():
g.get_workspace_base = get_workspace_base
g.get_workspace_dir = get_workspace_dir
g.get_workspace_path = get_workspace_path
g.get_space_dir = get_space_dir
g.get_project_dir = get_project_dir
g.get_project_apps_modules = get_project_apps_modules
g.build_workspace_path = build_workspace_path
g.build_space_path = build_space_path
g.resolve_workspace_path = resolve_workspace_path
g.GENERAL_SPACE = GENERAL_SPACE
g.build_tree_items = build_tree_items
g.build_file_widgets = build_file_widgets