CadHubManage/websocket-file-api-docs.md

29 KiB
Raw Blame History

WebSocket 文件管理API文档

概述

通过WebSocket连接实现CAD文件的列表查询和下载功能。支持Creo、PDMS、Revit三种CAD软件的文件格式。

WebSocket连接

连接地址: ws://localhost:8000/api/v1/ws/connect

连接参数:

  • client_id: 客户端ID可选不提供会自动生成
  • user_id: 用户ID可选

连接示例:

const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');

支持的文件格式

📐 Creo 文件

  • .prt - 零件文件Part
  • .asm - 装配文件Assembly
  • .drw - 工程图文件Drawing
  • 特别支持版本号后缀.prt.1, .prt.2, .asm.1

🏭 PDMS 文件

  • .rvm - 3D模型文件
  • .dri - 图纸文件

🏗️ Revit 文件

  • .rvt - Revit项目文件
  • .rfa - Revit族文件Family
  • .rte - Revit模板文件

WebSocket消息格式

1. 获取CAD文件列表

发送消息:

{
  "type": "get_file_list"
}

响应消息:

{
  "type": "info",
  "message": "获取文件列表成功",
  "data": {
    "base_path": "C:\\Users\\Public\\Documents",
    "total_count": 15,
    "files": [
      {
        "filename": "part1.prt.1",
        "relative_path": "creo/part1.prt.1",
        "absolute_path": "C:\\Users\\Public\\Documents\\creo\\part1.prt.1",
        "size": 1024000,
        "modified_time": 1706950800.0,
        "extension": ".prt.1"
      },
      {
        "filename": "building.rvt",
        "relative_path": "revit/building.rvt",
        "absolute_path": "C:\\Users\\Public\\Documents\\revit\\building.rvt",
        "size": 5120000,
        "modified_time": 1706951000.0,
        "extension": ".rvt"
      }
    ]
  },
  "timestamp": "2026-02-04T10:30:00"
}

2. 获取单个文件下载链接

发送消息:

{
  "type": "download_file",
  "file_path": "creo/part1.prt.1"
}

响应消息:

{
  "type": "info",
  "message": "文件下载链接已生成",
  "data": {
    "file_path": "creo/part1.prt.1",
    "filename": "part1.prt.1",
    "download_url": "/api/v1/files/download/creo/part1.prt.1",
    "file_size": 1024000
  },
  "timestamp": "2026-02-04T10:30:00"
}

使用下载链接:

// 获取到download_url后使用HTTP GET请求下载
const downloadUrl = `http://localhost:8000${data.download_url}`;
window.open(downloadUrl, '_blank');

3. 获取批量下载链接

发送消息:

{
  "type": "download_batch",
  "file_paths": [
    "creo/part1.prt.1",
    "creo/assembly.asm",
    "revit/building.rvt"
  ]
}

响应消息:

{
  "type": "info",
  "message": "批量下载链接已生成",
  "data": {
    "file_count": 3,
    "file_paths": [
      "creo/part1.prt.1",
      "creo/assembly.asm",
      "revit/building.rvt"
    ],
    "download_url": "/api/v1/files/download/batch",
    "method": "POST",
    "note": "请使用POST方法将file_paths作为JSON数组发送到此URL"
  },
  "timestamp": "2026-02-04T10:30:00"
}

使用批量下载:

// 使用fetch API进行POST请求
const response = await fetch('http://localhost:8000/api/v1/files/download/batch', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data.file_paths)
});

const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();

完整前端示例

JavaScript/WebSocket

class CADFileManager {
  constructor(serverUrl = 'ws://localhost:8000/api/v1/ws/connect') {
    this.ws = null;
    this.serverUrl = serverUrl;
    this.httpBaseUrl = 'http://localhost:8000';
  }
  
  // 连接WebSocket
  connect(userId = 'user123') {
    return new Promise((resolve, reject) => {
      this.ws = new WebSocket(`${this.serverUrl}?user_id=${userId}`);
      
      this.ws.onopen = () => {
        console.log('WebSocket连接成功');
        resolve();
      };
      
      this.ws.onerror = (error) => {
        console.error('WebSocket错误:', error);
        reject(error);
      };
      
      this.ws.onmessage = (event) => {
        const message = JSON.parse(event.data);
        this.handleMessage(message);
      };
    });
  }
  
  // 处理服务器消息
  handleMessage(message) {
    console.log('收到消息:', message);
    
    if (message.type === 'info' && message.data) {
      // 处理不同类型的响应
      if (message.data.files) {
        this.onFileListReceived(message.data);
      } else if (message.data.download_url) {
        this.onDownloadUrlReceived(message.data);
      }
    } else if (message.type === 'error') {
      console.error('错误:', message.message);
    }
  }
  
  // 获取文件列表
  getFileList() {
    this.ws.send(JSON.stringify({
      type: 'get_file_list'
    }));
  }
  
  // 文件列表回调(可以被覆盖)
  onFileListReceived(data) {
    console.log('文件列表:', data.files);
    console.log('总数:', data.total_count);
  }
  
  // 请求单个文件下载
  requestDownload(filePath) {
    this.ws.send(JSON.stringify({
      type: 'download_file',
      file_path: filePath
    }));
  }
  
  // 下载URL回调
  onDownloadUrlReceived(data) {
    const fullUrl = `${this.httpBaseUrl}${data.download_url}`;
    console.log('下载链接:', fullUrl);
    
    // 自动触发下载
    window.open(fullUrl, '_blank');
  }
  
  // 请求批量下载
  async requestBatchDownload(filePaths) {
    // 批量下载直接使用HTTP POST
    const response = await fetch(`${this.httpBaseUrl}/api/v1/files/download/batch`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(filePaths)
    });
    
    const blob = await response.blob();
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'cad_files.zip';
    a.click();
    window.URL.revokeObjectURL(url);
  }
  
  // 断开连接
  disconnect() {
    if (this.ws) {
      this.ws.close();
    }
  }
}

// 使用示例
async function main() {
  const manager = new CADFileManager();
  
  // 连接
  await manager.connect('user123');
  
  // 自定义文件列表回调
  manager.onFileListReceived = (data) => {
    console.log(`找到 ${data.total_count} 个文件`);
    data.files.forEach(file => {
      console.log(`- ${file.filename} (${(file.size / 1024).toFixed(2)} KB)`);
    });
  };
  
  // 获取文件列表
  manager.getFileList();
  
  // 下载单个文件
  setTimeout(() => {
    manager.requestDownload('creo/part1.prt.1');
  }, 2000);
  
  // 批量下载
  setTimeout(async () => {
    await manager.requestBatchDownload([
      'creo/part1.prt.1',
      'revit/building.rvt'
    ]);
  }, 4000);
}

main();

React示例

import React, { useState, useEffect, useRef } from 'react';

function CADFileManager() {
  const [files, setFiles] = useState([]);
  const [selectedFiles, setSelectedFiles] = useState([]);
  const [connected, setConnected] = useState(false);
  const wsRef = useRef(null);
  
  useEffect(() => {
    // 连接WebSocket
    const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
    
    ws.onopen = () => {
      console.log('WebSocket连接成功');
      setConnected(true);
      
      // 连接成功后立即获取文件列表
      ws.send(JSON.stringify({ type: 'get_file_list' }));
    };
    
    ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      
      if (message.type === 'info' && message.data?.files) {
        setFiles(message.data.files);
      } else if (message.type === 'info' && message.data?.download_url) {
        // 自动打开下载链接
        window.open(`http://localhost:8000${message.data.download_url}`, '_blank');
      }
    };
    
    wsRef.current = ws;
    
    return () => {
      ws.close();
    };
  }, []);
  
  // 刷新文件列表
  const refreshFileList = () => {
    if (wsRef.current && connected) {
      wsRef.current.send(JSON.stringify({ type: 'get_file_list' }));
    }
  };
  
  // 下载单个文件
  const downloadFile = (filePath) => {
    if (wsRef.current && connected) {
      wsRef.current.send(JSON.stringify({
        type: 'download_file',
        file_path: filePath
      }));
    }
  };
  
  // 批量下载
  const downloadBatch = async () => {
    const response = await fetch('http://localhost:8000/api/v1/files/download/batch', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(selectedFiles)
    });
    
    const blob = await response.blob();
    const url = window.URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'cad_files.zip';
    a.click();
    window.URL.revokeObjectURL(url);
  };
  
  // 切换文件选择
  const toggleFileSelection = (filePath) => {
    setSelectedFiles(prev => 
      prev.includes(filePath)
        ? prev.filter(f => f !== filePath)
        : [...prev, filePath]
    );
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h1>CAD文件管理器</h1>
      
      <div style={{ marginBottom: '20px' }}>
        <span>连接状态: {connected ? '✅ 已连接' : '❌ 未连接'}</span>
        <button onClick={refreshFileList} style={{ marginLeft: '10px' }}>
          刷新列表
        </button>
        <button 
          onClick={downloadBatch} 
          disabled={selectedFiles.length === 0}
          style={{ marginLeft: '10px' }}
        >
          批量下载 ({selectedFiles.length})
        </button>
      </div>
      
      <div>
        <h2>文件列表 ( {files.length} )</h2>
        <table border="1" cellPadding="8" style={{ width: '100%' }}>
          <thead>
            <tr>
              <th>选择</th>
              <th>文件名</th>
              <th>路径</th>
              <th>大小</th>
              <th>扩展名</th>
              <th>操作</th>
            </tr>
          </thead>
          <tbody>
            {files.map(file => (
              <tr key={file.absolute_path}>
                <td>
                  <input 
                    type="checkbox"
                    checked={selectedFiles.includes(file.relative_path)}
                    onChange={() => toggleFileSelection(file.relative_path)}
                  />
                </td>
                <td>{file.filename}</td>
                <td>{file.relative_path}</td>
                <td>{(file.size / 1024).toFixed(2)} KB</td>
                <td>{file.extension}</td>
                <td>
                  <button onClick={() => downloadFile(file.relative_path)}>
                    下载
                  </button>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}

export default CADFileManager;

错误处理

错误响应格式:

{
  "type": "error",
  "message": "错误描述",
  "timestamp": "2026-02-04T10:30:00"
}

常见错误:

  • "缺少参数: file_path" - 未提供文件路径
  • "缺少参数: file_paths (必须是数组)" - 批量下载参数格式错误
  • "访问被拒绝" - 文件路径不安全
  • "文件不存在" - 请求的文件不存在
  • "只能下载CAD文件" - 尝试下载非CAD格式文件
  • "获取文件列表失败" - 扫描文件系统失败

注意事项

  1. WebSocket用于控制HTTP用于下载

    • 文件列表通过WebSocket返回
    • 文件下载使用HTTP接口因为WebSocket不适合传输大文件
  2. 路径安全

    • 所有文件路径都会进行安全检查
    • 只能访问配置目录内的文件
  3. 文件格式限制

    • 只能下载Creo、PDMS、Revit格式的文件
    • 自动识别带版本号的Creo文件
  4. 批量下载

    • 文件打包为ZIP格式
    • 保持原有目录结构
    • 自动跳过不存在或不安全的文件
  5. 配置路径

    • 默认路径:C:\Users\Public\Documents
    • 可在 configs/software_config.yaml 中修改

文件批量重命名功能

4. 获取重命名策略列表

发送消息:

{
  "type": "get_rename_strategies"
}

响应消息:

{
  "type": "info",
  "message": "获取重命名策略成功",
  "data": {
    "strategies": [
      {
        "name": "add_prefix",
        "description": "在文件名前添加前缀"
      },
      {
        "name": "add_suffix",
        "description": "在扩展名前添加后缀"
      },
      {
        "name": "sequence",
        "description": "使用序号重命名文件"
      },
      {
        "name": "replace_text",
        "description": "替换文件名中的文本"
      },
      {
        "name": "add_datetime",
        "description": "添加日期时间戳"
      },
      {
        "name": "change_case",
        "description": "转换文件名大小写"
      }
    ],
    "total_count": 6
  },
  "timestamp": "2026-02-04T11:10:00"
}

5. 预览重命名结果

发送消息:

{
  "type": "preview_rename",
  "file_paths": [
    "creo/part1.prt.1",
    "creo/assembly.asm",
    "revit/building.rvt"
  ],
  "strategy": "add_prefix",
  "params": {
    "prefix": "NEW_"
  }
}

响应消息:

{
  "type": "info",
  "message": "重命名预览生成成功",
  "data": {
    "strategy": "add_prefix",
    "params": {
      "prefix": "NEW_"
    },
    "preview": [
      {
        "original": "part1.prt.1",
        "new": "NEW_part1.prt.1",
        "success": true
      },
      {
        "original": "assembly.asm",
        "new": "NEW_assembly.asm",
        "success": true
      },
      {
        "original": "building.rvt",
        "new": "NEW_building.rvt",
        "success": true
      }
    ],
    "total_count": 3
  },
  "timestamp": "2026-02-04T11:10:00"
}

6. 执行批量重命名

发送消息:

{
  "type": "rename_files",
  "file_paths": [
    "creo/part1.prt.1",
    "creo/assembly.asm"
  ],
  "strategy": "sequence",
  "params": {
    "base_name": "component",
    "start_number": 1,
    "digits": 3,
    "separator": "_"
  }
}

响应消息:

{
  "type": "info",
  "message": "批量重命名完成: 成功 2 个, 失败 0 个",
  "data": {
    "strategy": "sequence",
    "params": {
      "base_name": "component",
      "start_number": 1,
      "digits": 3,
      "separator": "_"
    },
    "success_count": 2,
    "failed_count": 0,
    "results": [
      {
        "original_path": "creo/part1.prt.1",
        "original_name": "part1.prt.1",
        "new_name": "component_001.prt.1",
        "new_path": "creo/component_001.prt.1",
        "success": true
      },
      {
        "original_path": "creo/assembly.asm",
        "original_name": "assembly.asm",
        "new_name": "component_002.asm",
        "new_path": "creo/component_002.asm",
        "success": true
      }
    ]
  },
  "timestamp": "2026-02-04T11:10:00"
}

重命名策略参数说明

1. add_prefix添加前缀

{
  "strategy": "add_prefix",
  "params": {
    "prefix": "NEW_"
  }
}

2. add_suffix添加后缀

{
  "strategy": "add_suffix",
  "params": {
    "suffix": "_backup"
  }
}

3. sequence序号重命名

{
  "strategy": "sequence",
  "params": {
    "base_name": "file",
    "start_number": 1,
    "digits": 3,
    "separator": "_"
  }
}
  • base_name: 基础文件名
  • start_number: 起始序号默认1
  • digits: 序号位数默认3如001
  • separator: 分隔符(默认下划线)

4. replace_text文本替换

{
  "strategy": "replace_text",
  "params": {
    "search": "old",
    "replace": "new",
    "case_sensitive": true
  }
}
  • search: 要搜索的文本
  • replace: 替换为的文本
  • case_sensitive: 是否区分大小写默认true

5. add_datetime添加日期时间

{
  "strategy": "add_datetime",
  "params": {
    "format": "%Y%m%d_%H%M%S",
    "position": "suffix",
    "separator": "_"
  }
}
  • format: 日期时间格式(默认:%Y%m%d_%H%M%S
  • position: 位置,prefixsuffix默认suffix
  • separator: 分隔符(默认下划线)

6. change_case大小写转换

{
  "strategy": "change_case",
  "params": {
    "case_type": "lower"
  }
}
  • case_type: 转换类型,可选值:lower(小写)、upper(大写)、title(标题格式)

重命名功能使用示例

JavaScript示例

// 扩展之前的 CADFileManager 类
class CADFileManager {
  // ... 之前的代码 ...
  
  // 获取重命名策略列表
  getRenameStrategies() {
    this.ws.send(JSON.stringify({
      type: 'get_rename_strategies'
    }));
  }
  
  // 预览重命名
  previewRename(filePaths, strategy, params) {
    this.ws.send(JSON.stringify({
      type: 'preview_rename',
      file_paths: filePaths,
      strategy: strategy,
      params: params
    }));
  }
  
  // 执行批量重命名
  renameFiles(filePaths, strategy, params) {
    this.ws.send(JSON.stringify({
      type: 'rename_files',
      file_paths: filePaths,
      strategy: strategy,
      params: params
    }));
  }
}

// 使用示例
async function renameExample() {
  const manager = new CADFileManager();
  await manager.connect('user123');
  
  // 1. 获取策略列表
  manager.getRenameStrategies();
  
  // 2. 预览重命名(添加前缀)
  setTimeout(() => {
    manager.previewRename(
      ['creo/part1.prt.1', 'creo/assembly.asm'],
      'add_prefix',
      { prefix: 'V2_' }
    );
  }, 1000);
  
  // 3. 执行重命名(序号重命名)
  setTimeout(() => {
    manager.renameFiles(
      ['creo/part1.prt.1', 'creo/part2.prt.1'],
      'sequence',
      {
        base_name: 'component',
        start_number: 1,
        digits: 3,
        separator: '_'
      }
    );
  }, 3000);
}

React示例

function CADFileRenamer() {
  const [files, setFiles] = useState([]);
  const [selectedFiles, setSelectedFiles] = useState([]);
  const [strategies, setStrategies] = useState([]);
  const [selectedStrategy, setSelectedStrategy] = useState('');
  const [renameParams, setRenameParams] = useState({});
  const [previewResults, setPreviewResults] = useState([]);
  const wsRef = useRef(null);
  
  useEffect(() => {
    const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
    
    ws.onopen = () => {
      // 获取文件列表和策略列表
      ws.send(JSON.stringify({ type: 'get_file_list' }));
      ws.send(JSON.stringify({ type: 'get_rename_strategies' }));
    };
    
    ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      
      if (message.type === 'info') {
        if (message.data?.files) {
          setFiles(message.data.files);
        } else if (message.data?.strategies) {
          setStrategies(message.data.strategies);
        } else if (message.data?.preview) {
          setPreviewResults(message.data.preview);
        } else if (message.data?.results) {
          // 重命名完成,刷新文件列表
          ws.send(JSON.stringify({ type: 'get_file_list' }));
          setPreviewResults([]);
          setSelectedFiles([]);
        }
      }
    };
    
    wsRef.current = ws;
    return () => ws.close();
  }, []);
  
  // 预览重命名
  const handlePreview = () => {
    if (wsRef.current && selectedFiles.length > 0 && selectedStrategy) {
      wsRef.current.send(JSON.stringify({
        type: 'preview_rename',
        file_paths: selectedFiles,
        strategy: selectedStrategy,
        params: renameParams
      }));
    }
  };
  
  // 执行重命名
  const handleRename = () => {
    if (wsRef.current && selectedFiles.length > 0 && selectedStrategy) {
      if (confirm(`确定要重命名 ${selectedFiles.length} 个文件吗?`)) {
        wsRef.current.send(JSON.stringify({
          type: 'rename_files',
          file_paths: selectedFiles,
          strategy: selectedStrategy,
          params: renameParams
        }));
      }
    }
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h1>CAD文件批量重命名</h1>
      
      {/* 策略选择 */}
      <div style={{ marginBottom: '20px' }}>
        <label>重命名策略: </label>
        <select 
          value={selectedStrategy} 
          onChange={(e) => setSelectedStrategy(e.target.value)}
        >
          <option value="">请选择策略</option>
          {strategies.map(s => (
            <option key={s.name} value={s.name}>{s.description}</option>
          ))}
        </select>
      </div>
      
      {/* 参数输入(根据策略动态显示) */}
      {selectedStrategy === 'add_prefix' && (
        <div>
          <label>前缀: </label>
          <input 
            type="text" 
            value={renameParams.prefix || ''} 
            onChange={(e) => setRenameParams({...renameParams, prefix: e.target.value})}
          />
        </div>
      )}
      
      {/* 操作按钮 */}
      <div style={{ marginBottom: '20px' }}>
        <button onClick={handlePreview} disabled={selectedFiles.length === 0}>
          预览重命名
        </button>
        <button onClick={handleRename} disabled={selectedFiles.length === 0} style={{ marginLeft: '10px' }}>
          执行重命名
        </button>
      </div>
      
      {/* 预览结果 */}
      {previewResults.length > 0 && (
        <div style={{ marginBottom: '20px', padding: '10px', background: '#f0f0f0' }}>
          <h3>预览结果</h3>
          {previewResults.map((result, idx) => (
            <div key={idx}>
              {result.original}  {result.new}
            </div>
          ))}
        </div>
      )}
      
      {/* 文件列表 */}
      <h2>文件列表</h2>
      <table border="1" cellPadding="8">
        <thead>
          <tr>
            <th>选择</th>
            <th>文件名</th>
            <th>路径</th>
          </tr>
        </thead>
        <tbody>
          {files.map(file => (
            <tr key={file.absolute_path}>
              <td>
                <input 
                  type="checkbox"
                  checked={selectedFiles.includes(file.relative_path)}
                  onChange={(e) => {
                    if (e.target.checked) {
                      setSelectedFiles([...selectedFiles, file.relative_path]);
                    } else {
                      setSelectedFiles(selectedFiles.filter(f => f !== file.relative_path));
                    }
                  }}
                />
              </td>
              <td>{file.filename}</td>
              <td>{file.relative_path}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

重命名功能注意事项

  1. 文件名冲突处理

    • 如果重命名后的文件名已存在,系统会自动添加数字后缀(如 file_1, file_2
    • 最多尝试1000次避免无限循环
  2. 扩展名保留

    • 所有重命名策略都会保留原有的文件扩展名
    • 支持带版本号的扩展名(如 .prt.1, .prt.2
  3. 预览功能

    • 建议在执行重命名前先使用预览功能
    • 预览不会修改实际文件,只返回重命名结果
  4. 错误处理

    • 批量重命名时,单个文件失败不会影响其他文件
    • 返回结果中会详细说明每个文件的成功/失败状态
  5. 安全性

    • 只能重命名配置目录内的CAD文件
    • 路径安全检查防止目录遍历攻击

配置管理功能

7. 获取文件配置

发送消息:

{
  "type": "get_file_config"
}

响应消息:

{
  "type": "info",
  "message": "获取文件配置成功",
  "data": {
    "base_path": "C:\\Users\\Public\\Documents",
    "file_extensions": {
      "creo": [".prt", ".asm", ".drw"],
      "pdms": [".rvm", ".dri"],
      "revit": [".rvt", ".rfa", ".rte"]
    }
  },
  "timestamp": "2026-02-04T14:30:00"
}

8. 设置文件基础路径

发送消息:

{
  "type": "set_base_path",
  "base_path": "D:\\CAD_Files"
}

响应消息(成功):

{
  "type": "info",
  "message": "基础路径设置成功",
  "data": {
    "base_path": "D:\\CAD_Files"
  },
  "timestamp": "2026-02-04T14:30:00"
}

响应消息(失败):

{
  "type": "error",
  "message": "设置基础路径失败: 路径不存在: D:\\Invalid_Path",
  "timestamp": "2026-02-04T14:30:00"
}

9. 设置文件扩展名

发送消息:

{
  "type": "set_file_extensions",
  "file_extensions": {
    "creo": [".prt", ".asm", ".drw", ".sec"],
    "pdms": [".rvm", ".dri"],
    "revit": [".rvt", ".rfa", ".rte"],
    "autocad": [".dwg", ".dxf"]
  }
}

响应消息(成功):

{
  "type": "info",
  "message": "文件扩展名配置已更新",
  "data": {
    "file_extensions": {
      "creo": [".prt", ".asm", ".drw", ".sec"],
      "pdms": [".rvm", ".dri"],
      "revit": [".rvt", ".rfa", ".rte"],
      "autocad": [".dwg", ".dxf"]
    }
  },
  "timestamp": "2026-02-04T14:30:00"
}

响应消息(失败):

{
  "type": "error",
  "message": "设置文件扩展名失败: 扩展名格式错误: dwg必须以'.'开头",
  "timestamp": "2026-02-04T14:30:00"
}

配置管理功能使用示例

JavaScript示例

// 扩展之前的 CADFileManager 类
class CADFileManager {
  // ... 之前的代码 ...
  
  // 获取文件配置
  getFileConfig() {
    this.ws.send(JSON.stringify({
      type: 'get_file_config'
    }));
  }
  
  // 设置基础路径
  setBasePath(basePath) {
    this.ws.send(JSON.stringify({
      type: 'set_base_path',
      base_path: basePath
    }));
  }
  
  // 设置文件扩展名
  setFileExtensions(extensions) {
    this.ws.send(JSON.stringify({
      type: 'set_file_extensions',
      file_extensions: extensions
    }));
  }
}

// 使用示例
async function configExample() {
  const manager = new CADFileManager();
  await manager.connect('user123');
  
  // 1. 获取当前配置
  manager.getFileConfig();
  
  // 2. 修改基础路径
  setTimeout(() => {
    manager.setBasePath('D:\\CAD_Files');
  }, 1000);
  
  // 3. 修改文件扩展名添加AutoCAD支持
  setTimeout(() => {
    manager.setFileExtensions({
      creo: ['.prt', '.asm', '.drw'],
      pdms: ['.rvm', '.dri'],
      revit: ['.rvt', '.rfa', '.rte'],
      autocad: ['.dwg', '.dxf']
    });
  }, 2000);
}

React示例

function CADConfigManager() {
  const [basePath, setBasePath] = useState('');
  const [fileExtensions, setFileExtensions] = useState({});
  const [newBasePath, setNewBasePath] = useState('');
  const wsRef = useRef(null);
  
  useEffect(() => {
    const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
    
    ws.onopen = () => {
      // 连接成功后获取当前配置
      ws.send(JSON.stringify({ type: 'get_file_config' }));
    };
    
    ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      
      if (message.type === 'info' && message.data) {
        if (message.data.base_path) {
          setBasePath(message.data.base_path);
        }
        if (message.data.file_extensions) {
          setFileExtensions(message.data.file_extensions);
        }
      }
    };
    
    wsRef.current = ws;
    return () => ws.close();
  }, []);
  
  // 修改基础路径
  const handleSetBasePath = () => {
    if (wsRef.current && newBasePath) {
      wsRef.current.send(JSON.stringify({
        type: 'set_base_path',
        base_path: newBasePath
      }));
    }
  };
  
  // 修改文件扩展名
  const handleSetExtensions = (software, extensions) => {
    const newExtensions = {
      ...fileExtensions,
      [software]: extensions
    };
    
    if (wsRef.current) {
      wsRef.current.send(JSON.stringify({
        type: 'set_file_extensions',
        file_extensions: newExtensions
      }));
    }
  };
  
  return (
    <div style={{ padding: '20px' }}>
      <h1>CAD配置管理器</h1>
      
      <div style={{ marginBottom: '20px' }}>
        <h2>当前基础路径</h2>
        <p>{basePath}</p>
        
        <input 
          type="text" 
          placeholder="输入新路径" 
          value={newBasePath}
          onChange={(e) => setNewBasePath(e.target.value)}
        />
        <button onClick={handleSetBasePath}>修改路径</button>
      </div>
      
      <div>
        <h2>当前文件扩展名配置</h2>
        <pre>{JSON.stringify(fileExtensions, null, 2)}</pre>
      </div>
    </div>
  );
}

配置管理功能注意事项

  1. 路径验证

    • 设置基础路径前会验证路径是否存在
    • 路径必须是有效的目录,不能是文件
  2. 扩展名格式

    • 所有扩展名必须以.开头
    • 扩展名配置必须是字典格式,值必须是数组
  3. 配置持久化

    • 所有配置修改会立即写入configs/software_config.yaml文件
    • 配置修改后会立即生效,影响文件列表和下载功能
  4. 权限要求

    • 修改配置需要对配置文件有写入权限
    • 建议在管理员权限下运行
  5. 配置同步

    • 修改配置后,建议重新获取文件列表以查看变更效果
    • 多个客户端同时修改配置时,以最后一次修改为准