temp:临时备份,测试合并兼容性

This commit is contained in:
PingChuan
2025-11-20 20:36:42 +08:00
parent 2e604e8ede
commit b9fe57c5fa
12 changed files with 1310 additions and 36 deletions
+63
View File
@@ -0,0 +1,63 @@
/**
* Collabora 配置生成 API 路由
*
* 功能:
* - 生成 Collabora iframe URL
* - 生成 WOPI access token
* - 返回完整的 Collabora 配置
*
* @encoding UTF-8
*/
import { type LoaderFunctionArgs, json } from '@remix-run/node';
import { getUserSession } from '~/api/login/auth.server';
import { generateCollaboraConfig } from '~/lib/collabora/config.server';
/**
* GET /api/collabora/config
*
* 查询参数:
* - fileId: 文件路径(例如:contracts/test.docx
* - mode: 模式(view 或 edit),默认 view
* - userId: 用户 ID(可选,从 session 获取)
* - userName: 用户名(可选,从 session 获取)
*/
export async function loader({ request }: LoaderFunctionArgs) {
try {
// 获取用户会话信息
const { userInfo } = await getUserSession(request);
// 解析查询参数
const url = new URL(request.url);
const fileId = url.searchParams.get('fileId');
const mode = (url.searchParams.get('mode') || 'view') as 'view' | 'edit';
const userId = url.searchParams.get('userId') || userInfo?.sub || 'guest';
const userName = url.searchParams.get('userName') || userInfo?.nick_name || '访客';
// 验证必需参数
if (!fileId) {
return json(
{ error: '文件路径不能为空' },
{ status: 400 }
);
}
// 生成 Collabora 配置
const config = await generateCollaboraConfig({
fileId,
mode,
userId,
userName,
});
return json(config);
} catch (error) {
console.error('生成 Collabora 配置失败:', error);
return json(
{
error: error instanceof Error ? error.message : '生成配置失败',
},
{ status: 500 }
);
}
}
@@ -0,0 +1,98 @@
/**
* WOPI 协议 API 路由
*
* 功能:
* - CheckFileInfo: GET /api/collabora/wopi/files/{fileId}
* - GetFile: GET /api/collabora/wopi/files/{fileId}/contents
* - PutFile: POST /api/collabora/wopi/files/{fileId}/contents
*
* @encoding UTF-8
*/
import { type LoaderFunctionArgs, type ActionFunctionArgs } from '@remix-run/node';
import { WopiService } from '~/lib/collabora/wopi.server';
const wopiService = new WopiService();
/**
* GET 请求处理
* - 无 /contents 后缀 → CheckFileInfo
* - 有 /contents 后缀 → GetFile
*/
export async function loader({ request, params }: LoaderFunctionArgs) {
try {
const url = new URL(request.url);
const accessToken = url.searchParams.get('access_token');
if (!accessToken) {
return new Response('访问令牌缺失', { status: 401 });
}
// 获取文件 ID
const fileId = params.fileId || '';
// 判断是否是 GetFile 请求(路径以 /contents 结尾)
const isContentsRequest = url.pathname.endsWith('/contents');
if (isContentsRequest) {
// GetFile: 返回文件内容
const { buffer, metadata } = await wopiService.getFile(fileId, accessToken);
return new Response(buffer, {
headers: {
'Content-Type': metadata.contentType,
'Content-Length': metadata.size.toString(),
'Content-Disposition': 'inline',
},
});
}
// CheckFileInfo: 返回文件元数据
const checkFileInfo = await wopiService.checkFileInfo(fileId, accessToken);
// 注意:CheckFileInfo 必须返回纯 JSON,不能使用 Result.success() 包装
return Response.json(checkFileInfo);
} catch (error) {
console.error('WOPI GET 失败:', error);
return new Response(
error instanceof Error ? error.message : 'Internal server error',
{ status: 500 }
);
}
}
/**
* POST 请求处理
* - PutFile: 保存文件内容
*/
export async function action({ request, params }: ActionFunctionArgs) {
try {
const url = new URL(request.url);
const accessToken = url.searchParams.get('access_token');
if (!accessToken) {
return new Response('访问令牌缺失', { status: 401 });
}
const fileId = params.fileId || '';
// 判断是否是 PutFile 请求(路径以 /contents 结尾)
const isContentsRequest = url.pathname.endsWith('/contents');
if (!isContentsRequest) {
return new Response('PutFile 必须使用 /contents 路径', { status: 400 });
}
// PutFile: 保存文件
const fileBuffer = await request.arrayBuffer();
await wopiService.putFile(fileId, accessToken, fileBuffer);
return new Response(null, { status: 200 });
} catch (error) {
console.error('WOPI POST 失败:', error);
return new Response(
error instanceof Error ? error.message : 'Internal server error',
{ status: 500 }
);
}
}
+5 -3
View File
@@ -150,11 +150,13 @@ export default function ContractTemplateDetail() {
}; */
// 创建文件内容对象用于FilePreview组件
const fileContent = template.pdf_file_path ? {
// 优先使用原始文件路径(支持docx),如果没有则使用pdf_file_path
const previewPath = template.file_path || template.pdf_file_path;
const fileContent = previewPath ? {
title: template.title,
contractNumber: template.template_code,
// 使用pdf_file_path字段
path: template.pdf_file_path,
// 使用file_path以支持多种格式(docx/pdf
path: previewPath,
parties: {
partyA: {
name: '',