- office_read.dspy: 按扩展名分流——xlsx/xlsm调univer-office的xlsx-to-workbookdata 返回workbookData;docx原链路不变(顺带f-string改拼接合规) - office_save.dspy: workbookData/documentData双字段分流,xlsx调workbook-data-to-xlsx回写 - workspace_view/open.dspy: office_exts扩为['.docx','.xlsx','.xlsm'], xlsx与docx同享在线查看(mode=view只读)+在线编辑+下载按钮 - 配套: univer-office仓11d83bc(IWorkbookData双向转换器+sheets插件+44断言往返自测)
76 lines
3.0 KiB
Plaintext
76 lines
3.0 KiB
Plaintext
# office_read.dspy - 读取工作空间 docx 文件,转 markdown 返回给 univer-office 前端
|
||
import os
|
||
import aiohttp
|
||
|
||
file_id = (params_kw or {}).get('file', '').strip()
|
||
|
||
uid = await get_user()
|
||
if not uid:
|
||
uid = 'user-01'
|
||
|
||
session_id = (params_kw or {}).get('session_id', '') or ''
|
||
pipeline_id = (params_kw or {}).get('pipeline_id', '') or ''
|
||
|
||
dbname = get_module_dbname('pipeline-sdlc')
|
||
|
||
async with DBPools().sqlorContext(dbname) as sor:
|
||
# 产线隔离:跨产线项目视为无项目(与弹窗入口一致,防绕过)
|
||
project_dir, _ = await get_project_dir_pl(sor, uid, session_id, pipeline_id)
|
||
space_dir, _ = await get_space_dir(sor, uid, session_id)
|
||
# 通用会话(pipeline_id=_generic):锁用户专属目录 _general/{uid}
|
||
# (2026-09-17 补齐:此前缺该分支,通用助手在线编辑 docx 报「未指定文件或无工作空间」)
|
||
if pipeline_id == '_generic':
|
||
_gd = generic_workspace_dir(uid)
|
||
os.makedirs(_gd, exist_ok=True)
|
||
project_dir = _gd
|
||
space_dir = _gd
|
||
|
||
if not file_id or not space_dir:
|
||
return {"error": "未指定文件或无工作空间", "markdown": ""}
|
||
|
||
full_path = resolve_workspace_path(project_dir, space_dir, file_id)
|
||
|
||
# 路径穿越校验
|
||
real_ws = os.path.realpath(space_dir)
|
||
real_full = os.path.realpath(full_path)
|
||
if not real_full.startswith(real_ws + os.sep):
|
||
return {"error": "非法路径", "markdown": ""}
|
||
|
||
if not os.path.isfile(full_path):
|
||
return {"error": "文件不存在", "markdown": ""}
|
||
|
||
# 按扩展名分流:xlsx → IWorkbookData(表格),其余(docx)→ IDocumentData(文档)
|
||
# (2026-09-18 xlsx 在线查看/编辑接入)
|
||
_is_sheet = file_id.lower().endswith(('.xlsx', '.xlsm'))
|
||
|
||
# 读文件 bytes
|
||
with open(full_path, 'rb') as f:
|
||
file_bytes = f.read()
|
||
|
||
# 调 univer-office 转换(格式保真,跳过 markdown)
|
||
try:
|
||
timeout = aiohttp.ClientTimeout(total=60)
|
||
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||
if _is_sheet:
|
||
async with session.post(
|
||
'http://127.0.0.1:19091/convert/xlsx-to-workbookdata',
|
||
data=file_bytes,
|
||
headers={'Content-Type': 'application/octet-stream'},
|
||
) as resp:
|
||
if resp.status != 200:
|
||
return {"error": "转换失败 HTTP " + str(resp.status), "workbookData": None}
|
||
workbook_data = await resp.json()
|
||
return {"workbookData": workbook_data}
|
||
else:
|
||
async with session.post(
|
||
'http://127.0.0.1:19091/convert/docx-to-docdata',
|
||
data=file_bytes,
|
||
headers={'Content-Type': 'application/octet-stream'},
|
||
) as resp:
|
||
if resp.status != 200:
|
||
return {"error": "转换失败 HTTP " + str(resp.status), "documentData": None}
|
||
doc_data = await resp.json()
|
||
return {"documentData": doc_data}
|
||
except Exception as e:
|
||
return {"error": str(e), "documentData": None, "workbookData": None}
|