fix(office): univer-office 端口 9091→19091,避免与 pipeline worker 1 撞端口

This commit is contained in:
ymq 2026-08-21 13:13:30 +08:00
parent 42f3b7bf26
commit 9f931347db
2 changed files with 111 additions and 0 deletions

View File

@ -0,0 +1,48 @@
# 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'
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
if not file_id or not ws_dir:
return {"error": "未指定文件或无工作空间", "markdown": ""}
full_path = ws_dir + '/' + file_id
# 路径穿越校验
real_ws = os.path.realpath(ws_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": ""}
# 读 docx bytes
with open(full_path, 'rb') as f:
docx_bytes = f.read()
# 调 univer-office 转 IDocumentData格式保真跳过 markdown
try:
timeout = aiohttp.ClientTimeout(total=60)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(
'http://127.0.0.1:19091/convert/docx-to-docdata',
data=docx_bytes,
headers={'Content-Type': 'application/octet-stream'},
) as resp:
if resp.status != 200:
return {"error": f"转换失败 HTTP {resp.status}", "documentData": None}
doc_data = await resp.json()
return {"documentData": doc_data}
except Exception as e:
return {"error": str(e), "documentData": None}

View File

@ -0,0 +1,63 @@
# office_save.dspy - univer-office 编辑后保存IDocumentData -> docx 写回工作空间
import os
import json as _json
import aiohttp
file_id = (params_kw or {}).get('file', '').strip()
docdata_str = (params_kw or {}).get('documentData', '') or ''
uid = await get_user()
if not uid:
uid = 'user-01'
dbname = get_module_dbname('pipeline-sdlc')
async with DBPools().sqlorContext(dbname) as sor:
ws_dir, _ = await get_workspace_dir(sor, uid)
if not file_id or not ws_dir:
return {"success": False, "error": "未指定文件或无工作空间"}
if not docdata_str:
return {"success": False, "error": "缺少文档数据"}
try:
documentData = _json.loads(docdata_str)
except Exception as e:
return {"success": False, "error": f"文档数据解析失败: {e}"}
full_path = ws_dir + '/' + file_id
# 路径穿越校验
real_ws = os.path.realpath(ws_dir)
real_full = os.path.realpath(full_path)
if not real_full.startswith(real_ws + os.sep):
return {"success": False, "error": "非法路径"}
# 只允许覆盖已存在的 office 文件(防任意写入)
if not os.path.isfile(full_path):
return {"success": False, "error": "目标文件不存在"}
title = os.path.basename(full_path)
# 调 univer-office 生成 docxIDocumentData -> docx保留格式
try:
timeout = aiohttp.ClientTimeout(total=120)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(
'http://127.0.0.1:19091/convert/doc-data-to-docx',
json={'documentData': documentData, 'title': title},
) as resp:
if resp.status != 200:
return {"success": False, "error": f"导出失败 HTTP {resp.status}"}
docx_bytes = await resp.read()
except Exception as e:
return {"success": False, "error": str(e)}
# 写回工作空间
try:
with open(full_path, 'wb') as f:
f.write(docx_bytes)
return {"success": True, "bytes": len(docx_bytes)}
except Exception as e:
return {"success": False, "error": str(e)}