[Claude Workbench] Initial commit - preserving existing code

This commit is contained in:
Claude Workbench
2025-11-14 17:41:15 +08:00
commit 0f7bc05697
587 changed files with 103215 additions and 0 deletions

View File

@@ -0,0 +1,562 @@
# 管理端OSS文件上传接口文档
## 📋 概述
管理端OSS文件上传接口提供了完整的文件管理功能包括文件上传签名生成、文件删除、批量删除和文件信息查询。**管理端和用户端的文件存储在同一目录下**`user_img/`),便于统一管理。
### 基础信息
- **基础路径**: `/admin/oss`
- **权限要求**: 需要管理员或工作人员JWT Token
- **文件存储**: 与用户端共享同一目录 (`user_img/`)
- **最大文件**: 500MB
- **有效期**: 2小时
---
## 🔐 认证方式
所有管理端接口都需要在请求头中携带JWT Token
```http
Authorization: Bearer {your_admin_jwt_token}
```
---
## 📡 API接口列表
### 1. 生成OSS POST签名
**接口地址**: `POST /admin/oss/post-signature`
**功能描述**: 生成管理端文件上传的OSS POST签名支持多种文件格式和大文件上传。
#### 请求参数
```json
{
"fileName": "banner.jpg",
"directory": "banners",
"description": "Banner图片",
"fileCategory": "image",
"maxSizeMB": 50
}
```
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| fileName | string | ✅ | 文件名,包含扩展名 |
| directory | string | ❌ | 子目录名称不包含user_img前缀 |
| description | string | ❌ | 文件描述 |
| fileCategory | string | ❌ | 文件分类image/document/compressed/video/audio/other |
| maxSizeMB | integer | ❌ | 最大文件大小(MB)默认50MB最大500MB |
#### 响应示例
```json
{
"code": 200,
"message": "管理端POST签名生成成功",
"data": {
"version": "OSS4-HMAC-SHA256",
"policy": "eyJleHBpcmF0aW9uIjoiMjAyNC0xMi0yNVQxNDowMDowMC4wMDBaIi...",
"x_oss_credential": "LTAI5t7Cn8mLa9K8NQy7S9Vj/20241225/cn-hangzhou/oss/aliyun_v4_request",
"x_oss_date": "20241225T120000Z",
"signature": "a1b2c3d4e5f6789...",
"security_token": "",
"dir": "user_img/banners/",
"host": "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com",
"accessKeyId": "LTAI5t7Cn8mLa9K8NQy7S9Vj",
"adminId": "123",
"fileName": "banner.jpg",
"fileType": "image",
"maxFileSize": 52428800,
"maxFileSizeMB": 50,
"supportedFormats": [
"图片: jpg, jpeg, png, gif, bmp, webp, svg, ico, tiff",
"文档: pdf, txt, md, json, xml, csv, doc, docx, xls, xlsx, ppt, pptx",
"压缩包: zip, rar, 7z, tar, gz, bz2, xz",
"音频: mp3, wav, flac, aac, ogg, wma",
"视频: mp4, avi, mov, wmv, flv, mkv, webm",
"其他: html, css, js, sql, log"
],
"uploadTips": "支持常见图片格式建议使用JPG/PNG格式以获得更好的兼容性。"
}
}
```
---
### 2. 删除文件
**接口地址**: `DELETE /admin/oss/file`
**功能描述**: 删除指定的OSS文件。
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| objectKey | string | ✅ | 文件的完整路径user_img/banners/banner.jpg |
#### 请求示例
```http
DELETE /admin/oss/file?objectKey=user_img/banners/banner.jpg
Authorization: Bearer {admin_jwt_token}
```
#### 响应示例
```json
{
"code": 200,
"message": "操作成功",
"data": "文件删除成功"
}
```
---
### 3. 批量删除文件
**接口地址**: `POST /admin/oss/batch-delete`
**功能描述**: 批量删除多个OSS文件。
#### 请求参数
```json
[
"user_img/banners/banner1.jpg",
"user_img/banners/banner2.jpg",
"user_img/documents/file.pdf"
]
```
#### 响应示例
```json
{
"code": 200,
"message": "批量删除操作完成",
"data": {
"success": [
"user_img/banners/banner1.jpg",
"user_img/banners/banner2.jpg"
],
"failed": [
"user_img/documents/file.pdf"
],
"total": 3,
"successCount": 2,
"failedCount": 1
}
}
```
---
### 4. 获取文件信息
**接口地址**: `GET /admin/oss/file-info`
**功能描述**: 获取OSS文件的详细信息。
#### 请求参数
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| objectKey | string | ✅ | 文件的完整路径 |
#### 请求示例
```http
GET /admin/oss/file-info?objectKey=user_img/banners/banner.jpg
Authorization: Bearer {admin_jwt_token}
```
#### 响应示例
```json
{
"code": 200,
"message": "获取文件信息成功",
"data": {
"objectKey": "user_img/banners/banner.jpg",
"size": 1024000,
"lastModified": "2024-12-25T12:00:00.000Z",
"contentType": "image/jpeg"
}
}
```
---
### 5. 获取上传配置
**接口地址**: `GET /admin/oss/upload-config`
**功能描述**: 获取管理端文件上传的配置信息。
#### 响应示例
```json
{
"code": 200,
"message": "获取上传配置成功",
"data": {
"maxFileSize": 524288000,
"maxFileSizeMB": 500,
"supportedFormats": [
"图片: jpg, jpeg, png, gif, bmp, webp, svg, ico, tiff",
"文档: pdf, txt, md, json, xml, csv, doc, docx, xls, xlsx, ppt, pptx",
"压缩包: zip, rar, 7z, tar, gz, bz2, xz",
"音频: mp3, wav, flac, aac, ogg, wma",
"视频: mp4, avi, mov, wmv, flv, mkv, webm",
"其他: html, css, js, sql, log"
],
"uploadDirectories": [
"banners",
"images",
"documents",
"videos",
"audios",
"uploads"
],
"tips": "管理端支持多种文件格式最大支持500MB文件上传。文件将与用户端文件存储在同一目录下建议根据用途选择合适的子目录。"
}
}
```
---
## 💻 前端使用示例
### JavaScript/Vue.js 示例
```javascript
class AdminOssUploader {
constructor(baseURL, token) {
this.baseURL = baseURL;
this.token = token;
}
// 获取上传签名
async getUploadSignature(fileName, directory = 'uploads', maxSizeMB = 50) {
const response = await fetch(`${this.baseURL}/admin/oss/post-signature`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify({
fileName,
directory,
fileCategory: this.getFileCategory(fileName),
maxSizeMB
})
});
const result = await response.json();
if (result.code === 200) {
return result.data;
}
throw new Error(result.message);
}
// 上传文件到OSS
async uploadFile(file, directory = 'uploads') {
try {
// 1. 获取签名
const signature = await this.getUploadSignature(file.name, directory);
// 2. 构建FormData
const formData = new FormData();
formData.append('key', `${signature.dir}${this.generateFileName(file.name)}`);
formData.append('policy', signature.policy);
formData.append('x-oss-credential', signature.x_oss_credential);
formData.append('x-oss-date', signature.x_oss_date);
formData.append('x-oss-signature-version', signature.x_oss_signature_version);
formData.append('x-oss-signature', signature.x_oss_signature);
formData.append('success_action_status', '200');
formData.append('file', file);
// 3. 上传到OSS
const uploadResponse = await fetch(signature.host, {
method: 'POST',
body: formData
});
if (uploadResponse.ok) {
const uploadedUrl = `${signature.host}/${formData.get('key')}`;
return {
success: true,
url: uploadedUrl,
key: formData.get('key')
};
}
throw new Error('Upload failed');
} catch (error) {
console.error('Upload error:', error);
return { success: false, error: error.message };
}
}
// 删除文件
async deleteFile(objectKey) {
const response = await fetch(`${this.baseURL}/admin/oss/file?objectKey=${encodeURIComponent(objectKey)}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.token}`
}
});
const result = await response.json();
return result.code === 200;
}
// 批量删除文件
async batchDeleteFiles(objectKeys) {
const response = await fetch(`${this.baseURL}/admin/oss/batch-delete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.token}`
},
body: JSON.stringify(objectKeys)
});
const result = await response.json();
return result.data;
}
// 生成唯一文件名
generateFileName(originalName) {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2);
const ext = originalName.substring(originalName.lastIndexOf('.'));
return `${timestamp}_${random}${ext}`;
}
// 获取文件分类
getFileCategory(fileName) {
const ext = fileName.substring(fileName.lastIndexOf('.')).toLowerCase();
if (['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg'].includes(ext)) {
return 'image';
} else if (['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv'].includes(ext)) {
return 'video';
} else if (['.mp3', '.wav', '.flac', '.aac', '.ogg'].includes(ext)) {
return 'audio';
} else if (['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx'].includes(ext)) {
return 'document';
} else if (['.zip', '.rar', '.7z', '.tar', '.gz'].includes(ext)) {
return 'compressed';
}
return 'other';
}
}
// 使用示例
const uploader = new AdminOssUploader('https://your-api.com', 'your-admin-token');
// 上传Banner图片
document.getElementById('bannerInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
if (file) {
const result = await uploader.uploadFile(file, 'banners');
if (result.success) {
console.log('上传成功:', result.url);
} else {
console.error('上传失败:', result.error);
}
}
});
```
### React Hook 示例
```jsx
import { useState, useCallback } from 'react';
const useAdminOssUpload = (token) => {
const [uploading, setUploading] = useState(false);
const [progress, setProgress] = useState(0);
const uploadFile = useCallback(async (file, directory = 'uploads') => {
setUploading(true);
setProgress(0);
try {
// 获取签名
const response = await fetch('/admin/oss/post-signature', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
fileName: file.name,
directory,
maxSizeMB: Math.ceil(file.size / (1024 * 1024))
})
});
const { data: signature } = await response.json();
// 上传到OSS
const formData = new FormData();
const fileKey = `${signature.dir}${Date.now()}_${file.name}`;
formData.append('key', fileKey);
formData.append('policy', signature.policy);
formData.append('x-oss-credential', signature.x_oss_credential);
formData.append('x-oss-date', signature.x_oss_date);
formData.append('x-oss-signature-version', signature.x_oss_signature_version);
formData.append('x-oss-signature', signature.x_oss_signature);
formData.append('success_action_status', '200');
formData.append('file', file);
const uploadResponse = await fetch(signature.host, {
method: 'POST',
body: formData
});
if (uploadResponse.ok) {
setProgress(100);
return {
success: true,
url: `${signature.host}/${fileKey}`,
key: fileKey
};
}
throw new Error('Upload failed');
} catch (error) {
return { success: false, error: error.message };
} finally {
setUploading(false);
}
}, [token]);
return { uploadFile, uploading, progress };
};
// 使用示例
const AdminFileUpload = () => {
const token = localStorage.getItem('adminToken');
const { uploadFile, uploading } = useAdminOssUpload(token);
const handleUpload = async (e) => {
const file = e.target.files[0];
if (file) {
const result = await uploadFile(file, 'banners');
if (result.success) {
alert('上传成功: ' + result.url);
} else {
alert('上传失败: ' + result.error);
}
}
};
return (
<div>
<input type="file" onChange={handleUpload} disabled={uploading} />
{uploading && <p>上传中...</p>}
</div>
);
};
```
---
## 📁 目录结构说明
### 存储路径规则
- **基础目录**: `user_img/` (与用户端共享)
- **完整路径**: `user_img/{directory}/{filename}`
### 推荐目录结构
```
user_img/
├── banners/ # Banner图片
├── images/ # 通用图片
├── documents/ # 文档文件
├── videos/ # 视频文件
├── audios/ # 音频文件
├── uploads/ # 默认上传目录
└── {custom}/ # 自定义目录
```
### 文件命名建议
```javascript
// 推荐的文件命名格式
const generateFileName = (originalName) => {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2);
const ext = originalName.substring(originalName.lastIndexOf('.'));
return `${timestamp}_${random}${ext}`;
};
```
---
## ⚠️ 注意事项
### 文件大小限制
- **用户端**: 最大10MB
- **管理端**: 最大500MB (可通过maxSizeMB参数调整)
### 文件格式支持
- **图片**: jpg, jpeg, png, gif, bmp, webp, svg, ico, tiff
- **文档**: pdf, txt, md, json, xml, csv, doc, docx, xls, xlsx, ppt, pptx
- **压缩包**: zip, rar, 7z, tar, gz, bz2, xz
- **音频**: mp3, wav, flac, aac, ogg, wma
- **视频**: mp4, avi, mov, wmv, flv, mkv, webm
- **其他**: html, css, js, sql, log
### 安全性
- 所有管理端接口都需要JWT认证
- 文件类型严格验证
- 文件大小限制保护
- 操作日志完整记录
### 错误码
- **200**: 操作成功
- **400**: 请求参数错误
- **401**: 未授权访问
- **403**: 权限不足
- **404**: 文件不存在
- **500**: 服务器内部错误
---
## 🔄 与用户端的差异
| 特性 | 用户端 | 管理端 |
|------|--------|--------|
| **权限** | 无需认证 | 需要管理员Token |
| **文件大小** | 10MB | 500MB |
| **文件格式** | 基础格式 | 全格式支持 |
| **目录** | user_img/ | user_img/ (相同) |
| **有效期** | 1小时 | 2小时 |
| **管理功能** | 仅上传 | 完整CRUD |
---
## 📞 技术支持
如遇到问题,请检查:
1. JWT Token是否有效
2. 文件格式是否支持
3. 文件大小是否超限
4. 网络连接是否正常
5. OSS配置是否正确
---
*最后更新时间: 2024-12-25*