Files
sftp-manager/docs/11-API接口设计规范.md
liu 14289beb66 Initial commit
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-03 10:10:11 +08:00

966 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 模块11API接口设计规范
---
## 🎨 UI设计系统概览
> **完整设计系统文档请参考:** `UI设计系统.md`
### 核心设计原则
- **现代简约**:界面清晰,层次分明
- **专业高效**:减少操作步骤,提升工作效率
- **一致性**:统一的视觉语言和交互模式
- **可访问性**符合WCAG 2.1 AA标准
### 关键设计令牌
**颜色系统:**
- 主色:`#0d6efd`(操作按钮、选中状态)
- 成功:`#198754`(连接成功状态)
- 危险:`#dc3545`(删除操作、错误提示)
- 深灰:`#212529`(导航栏背景)
- 浅灰:`#e9ecef`(工具栏背景)
**字体系统:**
- 字体族:系统字体栈(-apple-system, Segoe UI, Roboto等
- 正文14px行高1.5
- 标题20-32px行高1.2-1.4
- 小号文字12px文件大小、日期等
**间距系统:**
- 基础单位8px
- 标准间距16px1rem
- 组件内边距8px-16px
**组件规范:**
- 导航栏高度48px深色背景
- 工具栏浅灰背景按钮间距8px
- 文件项最小高度44px悬停效果150ms
- 按钮圆角4px过渡150ms
**交互规范:**
- 悬停效果150ms过渡
- 触摸目标最小44x44px
- 键盘导航Tab、Enter、Delete、F2、F5、Esc
- 焦点状态2px蓝色轮廓
**响应式断点:**
- 移动端:< 768px双面板垂直排列
- 平板768px - 1024px
- 桌面:> 1024px标准布局
---
## 11.1 RESTful设计原则
### 11.1.1 URL设计规范
**设计原则:**
1. 使用名词复数形式(/api/files
2. 使用HTTP方法表示操作类型
3. 使用查询参数传递可选参数
4. 使用路径参数传递必需参数
**命名规范:**
- URL使用小写字母
- 单词之间使用连字符(-)分隔
- 资源名称使用复数形式
- 层级关系使用斜杠(/)分隔
**示例:**
```
✅ 正确:
GET /api/files/list
POST /api/files/upload
DELETE /api/files/{id}
❌ 错误:
GET /api/getFiles
POST /api/files/create
DELETE /api/deleteFile/123
```
### 11.1.2 HTTP方法映射
| 方法 | 操作 | 是否幂等 | 说明 | 示例 |
|------|------|---------|------|------|
| GET | 查询 | 是 | 获取资源列表或详情 | GET /api/files/list |
| POST | 创建 | 否 | 创建新资源 | POST /api/files/mkdir |
| PUT | 更新 | 是 | 完整更新资源 | PUT /api/files/rename |
| DELETE | 删除 | 是 | 删除资源 | DELETE /api/files/delete |
### 11.1.3 统一响应格式
**成功响应:**
```json
{
"success": true,
"message": "操作成功",
"data": {}
}
```
**失败响应:**
```json
{
"success": false,
"message": "操作失败",
"error": "错误详细信息"
}
```
**列表响应:**
```json
{
"success": true,
"message": "查询成功",
"data": [
{},
{},
{}
],
"total": 100
}
```
**分页响应:**
```json
{
"success": true,
"message": "查询成功",
"data": [],
"pagination": {
"page": 1,
"pageSize": 20,
"total": 100,
"totalPages": 5
}
}
```
## 11.2 完整API列表
### 11.2.1 连接管理API
#### 建立SFTP连接
**请求:**
```
POST /api/connection/connect
Content-Type: application/json
{
"name": "测试服务器",
"host": "192.168.1.100",
"port": 22,
"username": "root",
"password": "123456",
"privateKeyPath": "/path/to/private/key",
"passPhrase": "key_password",
"rootPath": "/home/user"
}
```
**响应:**
```json
{
"success": true,
"message": "连接成功",
"data": {
"sessionId": "sftp-12345678-1234-1234-1234-123456789abc"
}
}
```
**错误响应:**
```json
{
"success": false,
"message": "连接失败",
"error": "认证失败:用户名或密码错误"
}
```
#### 断开连接
**请求:**
```
POST /api/connection/disconnect
Content-Type: application/json
{
"sessionId": "sftp-12345678-1234-1234-1234-123456789abc"
}
```
**响应:**
```json
{
"success": true,
"message": "断开成功",
"data": null
}
```
#### 保存连接配置
**请求:**
```
POST /api/connection/save
Content-Type: application/json
{
"id": 1,
"name": "测试服务器",
"host": "192.168.1.100",
"port": 22,
"username": "root",
"password": "encrypted_password",
"privateKeyPath": "/path/to/private/key",
"passPhrase": "encrypted_key_password",
"rootPath": "/home/user",
"connectTimeout": 10000
}
```
**响应:**
```json
{
"success": true,
"message": "保存成功",
"data": {
"id": 1,
"name": "测试服务器",
"host": "192.168.1.100",
"port": 22,
"username": "root",
"createdAt": "2024-02-02T10:00:00",
"updatedAt": "2024-02-02T10:00:00"
}
}
```
#### 获取所有保存的连接
**请求:**
```
GET /api/connection/list
```
**响应:**
```json
{
"success": true,
"message": "查询成功",
"data": [
{
"id": 1,
"name": "测试服务器",
"host": "192.168.1.100",
"port": 22,
"username": "root",
"createdAt": "2024-02-02T10:00:00",
"updatedAt": "2024-02-02T10:00:00"
}
]
}
```
#### 获取指定连接
**请求:**
```
GET /api/connection/{id}
```
**响应:**
```json
{
"success": true,
"message": "查询成功",
"data": {
"id": 1,
"name": "测试服务器",
"host": "192.168.1.100",
"port": 22,
"username": "root",
"createdAt": "2024-02-02T10:00:00",
"updatedAt": "2024-02-02T10:00:00"
}
}
```
**错误响应(连接不存在):**
```json
{
"success": false,
"message": "连接不存在",
"error": "ID为1的连接不存在"
}
```
#### 删除连接配置
**请求:**
```
DELETE /api/connection/{id}
```
**响应:**
```json
{
"success": true,
"message": "删除成功",
"data": null
}
```
#### 获取活跃连接列表
**请求:**
```
GET /api/connection/active
```
**响应:**
```json
{
"success": true,
"message": "查询成功",
"data": {
"sftp-12345678-1234-1234-1234-123456789abc": {
"id": 1,
"name": "测试服务器",
"host": "192.168.1.100",
"port": 22,
"username": "root"
},
"sftp-87654321-4321-4321-4321-cba987654321": {
"id": 2,
"name": "生产服务器",
"host": "192.168.1.200",
"port": 22,
"username": "admin"
}
}
}
```
### 11.2.2 文件操作API
#### 列出文件
**请求:**
```
POST /api/files/list
Content-Type: application/json
{
"sessionId": "sftp-12345678-1234-1234-1234-123456789abc",
"path": "/home/user"
}
```
**响应:**
```json
{
"success": true,
"message": "查询成功",
"data": [
{
"name": "Documents",
"path": "/home/user/Documents",
"size": 0,
"isDirectory": true,
"modifiedTime": "2024-02-02T10:00:00",
"permissions": "drwxr-xr-x"
},
{
"name": "test.txt",
"path": "/home/user/test.txt",
"size": 1024,
"isDirectory": false,
"modifiedTime": "2024-02-02T10:00:00",
"permissions": "-rw-r--r--"
}
]
}
```
#### 获取文件信息
**请求:**
```
POST /api/files/info
Content-Type: application/json
{
"sessionId": "sftp-12345678-1234-1234-1234-123456789abc",
"path": "/home/user/test.txt"
}
```
**响应:**
```json
{
"success": true,
"message": "查询成功",
"data": {
"name": "test.txt",
"path": "/home/user/test.txt",
"size": 1024,
"isDirectory": false,
"modifiedTime": "2024-02-02T10:00:00",
"permissions": "-rw-r--r--"
}
}
```
#### 获取当前路径
**请求:**
```
GET /api/files/path?sessionId=sftp-12345678-1234-1234-1234-123456789abc
```
**响应:**
```json
{
"success": true,
"message": "查询成功",
"data": {
"path": "/home/user"
}
}
```
#### 上传文件
**请求:**
```
POST /api/files/upload
Content-Type: multipart/form-data
file: [文件内容]
targetSessionId: sftp-12345678-1234-1234-1234-123456789abc
targetPath: /home/user/
```
**响应:**
```json
{
"success": true,
"message": "上传成功",
"data": null
}
```
**错误响应:**
```json
{
"success": false,
"message": "上传失败",
"error": "目标目录不存在"
}
```
#### 下载文件
**请求:**
```
GET /api/files/download?sessionId=sftp-12345678-1234-1234-1234-123456789abc&path=/home/user/test.txt
```
**响应:**
```
[文件流]
```
**响应头:**
```
Content-Type: application/octet-stream
Content-Disposition: attachment; filename=test.txt
Content-Length: 1024
```
#### 服务器间传输
**请求:**
```
POST /api/files/transfer
Content-Type: application/json
{
"sourceSessionId": "sftp-12345678-1234-1234-1234-123456789abc",
"sourcePath": "/home/user/test.txt",
"targetSessionId": "sftp-87654321-4321-4321-4321-cba987654321",
"targetPath": "/home/target/"
}
```
**响应:**
```json
{
"success": true,
"message": "传输成功",
"data": null
}
```
#### 删除文件
**请求:**
```
DELETE /api/files/delete?sessionId=sftp-12345678-1234-1234-1234-123456789abc&path=/home/user/test.txt
```
**响应:**
```json
{
"success": true,
"message": "删除成功",
"data": null
}
```
#### 批量删除
**请求:**
```
POST /api/files/batch-delete
Content-Type: application/json
{
"sessionId": "local",
"paths": [
"C:/test/file1.txt",
"C:/test/file2.txt",
"C:/test/file3.txt"
]
}
```
**响应:**
```json
{
"success": true,
"message": "删除完成",
"data": {
"successCount": 2,
"failCount": 1,
"failedFiles": [
"C:/test/file3.txt - 没有删除权限"
]
}
}
```
#### 重命名
**请求:**
```
POST /api/files/rename
Content-Type: application/json
{
"sessionId": "sftp-12345678-1234-1234-1234-123456789abc",
"oldPath": "/home/user/old.txt",
"newPath": "/home/user/new.txt"
}
```
**响应:**
```json
{
"success": true,
"message": "重命名成功",
"data": null
}
```
#### 新建文件夹
**请求:**
```
POST /api/files/mkdir
Content-Type: application/json
{
"sessionId": "local",
"path": "C:/test/newfolder"
}
```
**响应:**
```json
{
"success": true,
"message": "创建成功",
"data": null
}
```
## 11.3 错误码定义
### 11.3.1 HTTP状态码
| 状态码 | 说明 | 使用场景 |
|--------|------|---------|
| 200 | OK | 请求成功 |
| 201 | Created | 资源创建成功 |
| 400 | Bad Request | 请求参数错误 |
| 401 | Unauthorized | 未授权(连接失败) |
| 403 | Forbidden | 权限不足 |
| 404 | Not Found | 资源不存在 |
| 500 | Internal Server Error | 服务器内部错误 |
### 11.3.2 业务错误码
```java
public enum ErrorCode {
SUCCESS(0, "成功"),
INVALID_PARAMETER(1001, "参数错误"),
CONNECTION_NOT_FOUND(2001, "连接不存在"),
CONNECTION_FAILED(2002, "连接失败"),
FILE_NOT_FOUND(3001, "文件不存在"),
FILE_ALREADY_EXISTS(3002, "文件已存在"),
INSUFFICIENT_PERMISSION(3003, "权限不足"),
DISK_SPACE_FULL(3004, "磁盘空间不足"),
OPERATION_FAILED(5000, "操作失败");
private final int code;
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
```
### 11.3.3 错误响应格式
```json
{
"success": false,
"message": "操作失败",
"error": {
"code": 3001,
"message": "文件不存在",
"details": "文件 /home/user/test.txt 不存在"
}
}
```
## 11.4 API版本管理
### 11.4.1 URL版本控制
```
/api/v1/connection/connect
/api/v2/connection/connect
```
### 11.4.2 请求头版本控制
```
GET /api/connection/list
Accept: application/vnd.sftp-manager.v1+json
```
### 11.4.3 查询参数版本控制
```
GET /api/connection/list?version=1
```
## 11.5 API文档生成
### 11.5.1 Swagger集成
```xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.14</version>
</dependency>
```
### 11.5.2 Swagger配置
```java
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI springShopOpenAPI() {
return new OpenAPI()
.info(new Info().title("SFTP文件管理系统API")
.description("SFTP文件管理系统REST API文档")
.version("v1.0.0"));
}
}
```
### 11.5.3 注解示例
```java
@RestController
@RequestMapping("/api/connection")
@Tag(name = "连接管理", description = "SFTP连接相关接口")
public class ConnectionController {
@Operation(summary = "建立SFTP连接", description = "根据连接参数建立SFTP连接")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "连接成功"),
@ApiResponse(responseCode = "400", description = "参数错误"),
@ApiResponse(responseCode = "500", description = "连接失败")
})
@PostMapping("/connect")
public ApiResponse<String> connect(
@Parameter(description = "连接参数", required = true)
@RequestBody ConnectionRequest request) {
// 实现
}
}
```
## 11.6 API测试
### 11.6.1 Postman测试
导入以下JSON到Postman
```json
{
"info": {
"name": "SFTP Manager API",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "连接管理",
"item": [
{
"name": "建立SFTP连接",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "application/json"
}
],
"url": {
"raw": "{{baseUrl}}/api/connection/connect",
"host": ["{{baseUrl}}"],
"path": ["api", "connection", "connect"]
},
"body": {
"mode": "raw",
"raw": "{\n \"name\": \"测试服务器\",\n \"host\": \"192.168.1.100\",\n \"port\": 22,\n \"username\": \"root\",\n \"password\": \"123456\"\n}"
}
}
}
]
}
]
}
```
### 11.6.2 curl测试
```bash
# 测试连接API
curl -X POST http://localhost:8080/sftp-manager/api/connection/connect \
-H "Content-Type: application/json" \
-d '{"name":"测试","host":"192.168.1.100","port":22,"username":"root","password":"123456"}'
# 测试文件列表API
curl -X POST http://localhost:8080/sftp-manager/api/files/list \
-H "Content-Type: application/json" \
-d '{"sessionId":"local","path":"C:/Users"}'
```
## 11.7 安全规范
### 11.7.1 认证与会话
- **Session 管理**:连接建立后服务端维护 `sessionId`,前端在后续请求中携带
- **敏感字段**:密码、密钥口令等不入库明文,保存连接配置时使用加密存储
- **请求头**:可在 Header 中传递 `X-Session-Id` 作为会话标识(与 body 中的 sessionId 二选一或并存校验)
### 11.7.2 CORS 配置
```java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOriginPatterns("http://localhost:*", "https://your-domain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
```
### 11.7.3 敏感数据脱敏
- 列表/详情接口返回连接信息时,不返回 `password``passPhrase`
- 日志中禁止打印密码、密钥内容;错误信息可返回“认证失败”等笼统描述
---
## 11.8 限流与性能建议
### 11.8.1 建议限流策略
| 接口类型 | 建议限流 |
|----------------|-----------------------------|
| 连接/断开 | 每 IP 每分钟 10 次 |
| 文件列表/信息 | 每 session 每秒 20 次 |
| 上传/下载 | 按连接数 + 单文件大小限制 |
| 删除/重命名 | 每 session 每秒 10 次 |
### 11.8.2 大文件与流式传输
- **上传**:使用 `multipart/form-data`,服务端流式写入,避免整文件进内存
- **下载**:使用 `InputStreamResource``StreamingResponseBody` 流式输出
- **服务器间传输**:服务端到服务端流式转发,不落本地盘(或使用临时缓冲区流式处理)
---
## 11.9 前端调用示例Vue 3 + Axios
### 11.9.1 封装 baseURL 与响应处理
```typescript
// api/client.ts
import axios, { type AxiosInstance } from 'axios';
const api: AxiosInstance = axios.create({
baseURL: '/sftp-manager/api',
timeout: 30000,
headers: { 'Content-Type': 'application/json' },
});
api.interceptors.response.use(
(res) => {
const { success, data, message } = res.data ?? {};
if (!success) return Promise.reject(new Error(message || '请求失败'));
return res.data;
},
(err) => Promise.reject(err.response?.data?.message || err.message)
);
export default api;
```
### 11.9.2 连接管理示例
```typescript
// api/connection.ts
import api from './client';
export interface ConnectParams {
name: string;
host: string;
port: number;
username: string;
password?: string;
privateKeyPath?: string;
passPhrase?: string;
rootPath?: string;
}
export function connect(params: ConnectParams) {
return api.post<{ data: { sessionId: string } }>('/connection/connect', params);
}
export function disconnect(sessionId: string) {
return api.post('/connection/disconnect', { sessionId });
}
export function getConnectionList() {
return api.get<{ data: Array<{ id: number; name: string; host: string; port: number; username: string }> }>('/connection/list');
}
```
### 11.9.3 文件操作示例
```typescript
// api/files.ts
import api from './client';
export interface FileItem {
name: string;
path: string;
size: number;
isDirectory: boolean;
modifiedTime: string;
permissions?: string;
}
export function listFiles(sessionId: string, path: string) {
return api.post<{ data: Array<FileItem> }>('/files/list', { sessionId, path });
}
export function uploadFile(sessionId: string, targetPath: string, file: File) {
const form = new FormData();
form.append('file', file);
form.append('targetSessionId', sessionId);
form.append('targetPath', targetPath);
return api.post('/files/upload', form, {
headers: { 'Content-Type': 'multipart/form-data' },
});
}
export function downloadFile(sessionId: string, path: string) {
return api.get('/files/download', {
params: { sessionId, path },
responseType: 'blob',
});
}
export function deleteFile(sessionId: string, path: string) {
return api.delete('/files/delete', { params: { sessionId, path } });
}
```
---
## 11.10 健康检查与就绪探针(可选)
便于容器化部署与负载均衡探测:
| 路径 | 方法 | 说明 |
|-------------------|------|----------------|
| `/actuator/health` | GET | 应用存活检查 |
| `/actuator/ready` | GET | 就绪(依赖就绪)|
---
## 注意事项
1. **统一响应格式**所有API使用统一的响应格式
2. **错误处理**:提供详细的错误信息
3. **参数验证**:严格验证请求参数
4. **安全性**:敏感数据加密传输
5. **性能**:大文件使用流式传输
## 下一步
完成模块11后继续模块12错误处理与日志