|
@@ -1,180 +0,0 @@
|
|
|
-import axios from 'axios';
|
|
|
-
|
|
|
-/**
|
|
|
- * 腾讯文档API交互工具类
|
|
|
- * 基于腾讯文档的非官方API实现数据获取
|
|
|
- */
|
|
|
-export interface TencentDocConfig {
|
|
|
- cookie: string;
|
|
|
- documentUrl: string;
|
|
|
-}
|
|
|
-
|
|
|
-export interface OperationResult {
|
|
|
- operationId: string;
|
|
|
- status: boolean;
|
|
|
- message?: string;
|
|
|
-}
|
|
|
-
|
|
|
-export interface ProgressResult {
|
|
|
- progress: number;
|
|
|
- file_url?: string;
|
|
|
- message?: string;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 从URL中提取文档ID
|
|
|
- * @param documentUrl 腾讯文档URL
|
|
|
- * @returns 文档ID
|
|
|
- */
|
|
|
-export function extractDocumentId(documentUrl: string): string {
|
|
|
- // 处理类似 https://docs.qq.com/sheet/DQVZpaFVqdnJFU3hn 的URL
|
|
|
- const urlParts = documentUrl.split('/');
|
|
|
- const lastPart = urlParts[urlParts.length - 1].split('?')[0];
|
|
|
- return lastPart;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 腾讯文档API客户端
|
|
|
- */
|
|
|
-export class TencentDocClient {
|
|
|
- private cookie: string;
|
|
|
- private documentId: string;
|
|
|
- private documentUrl: string;
|
|
|
-
|
|
|
- constructor(config: TencentDocConfig) {
|
|
|
- this.cookie = config.cookie;
|
|
|
- this.documentUrl = config.documentUrl;
|
|
|
- this.documentId = extractDocumentId(config.documentUrl);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 创建导出任务
|
|
|
- * @returns 操作结果,包含operationId
|
|
|
- */
|
|
|
- async createExportTask(): Promise<OperationResult> {
|
|
|
- try {
|
|
|
- const exportUrl = 'https://docs.qq.com/v1/export/export_office';
|
|
|
-
|
|
|
- const requestData = {
|
|
|
- docId: this.documentId,
|
|
|
- version: '2',
|
|
|
- };
|
|
|
-
|
|
|
- const headers = {
|
|
|
- 'content-type': 'application/x-www-form-urlencoded',
|
|
|
- 'Cookie': this.cookie,
|
|
|
- 'Referer': this.documentUrl
|
|
|
- };
|
|
|
-
|
|
|
- const response = await axios.post(exportUrl, requestData, { headers });
|
|
|
-
|
|
|
- if (response.data && response.data.operationId) {
|
|
|
- return {
|
|
|
- operationId: response.data.operationId,
|
|
|
- status: true
|
|
|
- };
|
|
|
- } else {
|
|
|
- throw new Error('获取operationId失败');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('创建导出任务失败:', error);
|
|
|
- return {
|
|
|
- operationId: '',
|
|
|
- status: false,
|
|
|
- message: error instanceof Error ? error.message : '未知错误'
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 查询导出进度
|
|
|
- * @param operationId 操作ID
|
|
|
- * @returns 进度结果
|
|
|
- */
|
|
|
- async queryExportProgress(operationId: string): Promise<ProgressResult> {
|
|
|
- try {
|
|
|
- const progressUrl = `https://docs.qq.com/v1/export/query_progress?operationId=${operationId}`;
|
|
|
-
|
|
|
- const headers = {
|
|
|
- 'Cookie': this.cookie,
|
|
|
- 'Referer': this.documentUrl
|
|
|
- };
|
|
|
-
|
|
|
- const response = await axios.get(progressUrl, { headers });
|
|
|
-
|
|
|
- if (response.data) {
|
|
|
- return {
|
|
|
- progress: response.data.progress || 0,
|
|
|
- file_url: response.data.file_url
|
|
|
- };
|
|
|
- } else {
|
|
|
- throw new Error('查询导出进度失败');
|
|
|
- }
|
|
|
- } catch (error) {
|
|
|
- console.error('查询导出进度失败:', error);
|
|
|
- return {
|
|
|
- progress: 0,
|
|
|
- message: error instanceof Error ? error.message : '未知错误'
|
|
|
- };
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 下载导出的文件
|
|
|
- * @param fileUrl 文件URL
|
|
|
- * @returns 文件的二进制数据
|
|
|
- */
|
|
|
- async downloadExportedFile(fileUrl: string): Promise<ArrayBuffer> {
|
|
|
- try {
|
|
|
- const response = await axios.get(fileUrl, {
|
|
|
- responseType: 'arraybuffer',
|
|
|
- headers: {
|
|
|
- 'Cookie': this.cookie
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return response.data;
|
|
|
- } catch (error) {
|
|
|
- console.error('下载文件失败:', error);
|
|
|
- throw error;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 完整的导出流程
|
|
|
- * @param maxRetries 最大重试次数
|
|
|
- * @param retryInterval 重试间隔(毫秒)
|
|
|
- * @returns 文件的二进制数据
|
|
|
- */
|
|
|
- async exportDocument(maxRetries = 10, retryInterval = 1000): Promise<ArrayBuffer> {
|
|
|
- // 创建导出任务
|
|
|
- const operationResult = await this.createExportTask();
|
|
|
- if (!operationResult.status) {
|
|
|
- throw new Error(`创建导出任务失败: ${operationResult.message}`);
|
|
|
- }
|
|
|
-
|
|
|
- const operationId = operationResult.operationId;
|
|
|
- let retries = 0;
|
|
|
- let fileUrl = '';
|
|
|
-
|
|
|
- // 轮询查询进度
|
|
|
- while (retries < maxRetries) {
|
|
|
- const progressResult = await this.queryExportProgress(operationId);
|
|
|
-
|
|
|
- if (progressResult.progress === 100 && progressResult.file_url) {
|
|
|
- fileUrl = progressResult.file_url;
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- retries++;
|
|
|
- await new Promise(resolve => setTimeout(resolve, retryInterval));
|
|
|
- }
|
|
|
-
|
|
|
- if (!fileUrl) {
|
|
|
- throw new Error('导出超时或未获取到文件下载地址');
|
|
|
- }
|
|
|
-
|
|
|
- // 下载文件
|
|
|
- return await this.downloadExportedFile(fileUrl);
|
|
|
- }
|
|
|
-}
|