[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,374 @@
# 详情图集功能使用指南
## 🎯 功能概述
详情图集功能允许为工作流和课程添加多张详情展示图片,为用户提供更丰富的视觉内容介绍。
## 🔧 技术实现
### 数据库字段
```sql
-- 工作流表
ALTER TABLE workflow ADD COLUMN detail_gallery longtext DEFAULT NULL
COMMENT '详情图集JSON格式存储多张图片URL';
-- 课程表
ALTER TABLE course ADD COLUMN detail_gallery longtext DEFAULT NULL
COMMENT '详情图集JSON格式存储多张图片URL';
```
### 存储格式
```json
// 详情图集字段存储格式
"detailGallery": "[\"https://oss.../image1.jpg\",\"https://oss.../image2.jpg\",\"https://oss.../image3.jpg\"]"
```
## 📱 前端集成
### 1. 图片上传流程
```javascript
/**
* 上传详情图集
* @param {FileList} files - 选择的图片文件
* @param {string} userId - 用户ID
* @returns {Promise<string>} 详情图集JSON字符串
*/
async function uploadDetailGallery(files, userId) {
const uploadPromises = Array.from(files).map(async (file) => {
// 1. 获取OSS上传签名
const signResponse = await fetch('/user/oss/post-signature/json', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
fileName: file.name,
userId: userId
})
});
if (!signResponse.ok) {
throw new Error('获取上传签名失败');
}
const signResult = await signResponse.json();
const signData = signResult.data;
// 2. 构建上传表单
const formData = new FormData();
const objectKey = signData.dir + file.name;
formData.append('key', objectKey);
formData.append('policy', signData.policy);
formData.append('x-oss-credential', signData.x_oss_credential);
formData.append('x-oss-date', signData.x_oss_date);
formData.append('x-oss-signature-version', signData.version);
formData.append('x-oss-signature', signData.signature);
formData.append('success_action_status', '200');
formData.append('file', file);
// 3. 上传到OSS
const uploadResponse = await fetch(signData.url, {
method: 'POST',
body: formData
});
if (!uploadResponse.ok) {
throw new Error('文件上传失败');
}
// 4. 返回完整的文件URL
return `${signData.url}/${objectKey}`;
});
try {
const imageUrls = await Promise.all(uploadPromises);
return JSON.stringify(imageUrls);
} catch (error) {
console.error('上传详情图集失败:', error);
throw error;
}
}
```
### 2. 解析详情图集
```javascript
/**
* 解析详情图集
* @param {string} detailGallery - 详情图集JSON字符串
* @returns {string[]} 图片URL数组
*/
function parseDetailGallery(detailGallery) {
if (!detailGallery || detailGallery.trim() === '') {
return [];
}
try {
const urls = JSON.parse(detailGallery);
return Array.isArray(urls) ? urls : [];
} catch (error) {
console.error('解析详情图集失败:', error);
return [];
}
}
/**
* 渲染详情图集
* @param {string} detailGallery - 详情图集JSON字符串
* @param {HTMLElement} container - 容器元素
*/
function renderDetailGallery(detailGallery, container) {
const imageUrls = parseDetailGallery(detailGallery);
container.innerHTML = '';
if (imageUrls.length === 0) {
container.innerHTML = '<p>暂无详情图片</p>';
return;
}
imageUrls.forEach((url, index) => {
const img = document.createElement('img');
img.src = url;
img.alt = `详情图片 ${index + 1}`;
img.className = 'detail-gallery-image';
img.style.cssText = `
width: 100%;
max-width: 400px;
height: auto;
margin: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
cursor: pointer;
`;
// 点击预览
img.addEventListener('click', () => {
showImagePreview(url);
});
container.appendChild(img);
});
}
```
### 3. 完整使用示例
```html
<!-- HTML -->
<div class="upload-section">
<label for="detail-images">选择详情图片(可选择多张):</label>
<input type="file" id="detail-images" multiple accept="image/*">
<button onclick="handleUpload()">上传作品</button>
</div>
<div id="preview-container"></div>
<script>
async function handleUpload() {
const fileInput = document.getElementById('detail-images');
const files = fileInput.files;
let detailGallery = '';
// 如果选择了图片,则上传详情图集
if (files.length > 0) {
try {
detailGallery = await uploadDetailGallery(files, getCurrentUserId());
console.log('详情图集上传成功:', detailGallery);
} catch (error) {
alert('详情图集上传失败: ' + error.message);
return;
}
}
// 提交工作流
const workflowData = {
name: "我的工作流",
description: "工作流描述",
detailGallery: detailGallery, // 详情图集
vodVideoId: "a0776b0179bf71f0bea45017f1e90102",
data: JSON.stringify({nodes: [], edges: []}),
isFree: 1
};
try {
const response = await fetch('/user/workflow/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + getToken()
},
body: JSON.stringify(workflowData)
});
if (response.ok) {
const result = await response.json();
alert('工作流上传成功ID: ' + result.data);
} else {
alert('工作流上传失败');
}
} catch (error) {
alert('提交失败: ' + error.message);
}
}
// 获取当前用户ID
function getCurrentUserId() {
return '17543607206742139'; // 示例用户ID
}
// 获取认证token
function getToken() {
return localStorage.getItem('token');
}
</script>
```
## 🚀 后端接口支持
### 1. 工作流相关接口
**上传工作流** - `POST /user/workflow/submit`
```json
{
"name": "工作流名称",
"detailGallery": "[\"url1\",\"url2\"]",
"vodVideoId": "视频ID",
"data": "工作流JSON数据"
}
```
**更新工作流** - `PUT /user/content/workflows/{id}`
```json
{
"name": "更新的名称",
"detailGallery": "[\"new_url1\",\"new_url2\"]"
}
```
### 2. 课程相关接口
**更新课程** - `PUT /user/course/{id}`
```json
{
"title": "课程标题",
"detailGallery": "[\"url1\",\"url2\"]",
"price": 99.99
}
```
**用户内容管理** - `PUT /user/content/courses`
```json
{
"id": 1,
"title": "课程标题",
"detailGallery": "[\"url1\",\"url2\"]"
}
```
## 📋 响应示例
### 工作流详情API响应
```json
{
"code": 200,
"message": "success",
"data": {
"workflow": {
"id": 1,
"name": "智能图像生成工作流",
"coverUrl": "https://oss.../cover.jpg",
"detailGallery": "[\"https://oss.../detail1.jpg\",\"https://oss.../detail2.jpg\",\"https://oss.../detail3.jpg\"]",
"vodVideoId": "a0776b0179bf71f0bea45017f1e90102",
"price": 29.99
}
}
}
```
### 课程详情API响应
```json
{
"id": 1,
"title": "AI图像处理入门课程",
"coverUrl": "https://oss.../cover.jpg",
"detailGallery": "[\"https://oss.../detail1.jpg\",\"https://oss.../detail2.jpg\"]",
"price": 99.99,
"chapters": [...]
}
```
## ⚙️ 最佳实践
### 1. 图片要求
- **尺寸**: 建议 1200x800px 或同等比例
- **格式**: 推荐 JPG/PNG
- **大小**: 单张图片不超过 5MB
- **数量**: 建议 2-5 张图片
### 2. 用户体验
- **预览功能**: 支持图片点击放大预览
- **加载优化**: 使用懒加载和图片压缩
- **错误处理**: 提供友好的错误提示
- **进度显示**: 显示上传进度
### 3. 性能优化
```javascript
// 图片压缩示例
function compressImage(file, maxWidth = 1200, quality = 0.8) {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = () => {
const ratio = Math.min(maxWidth / img.width, maxWidth / img.height);
canvas.width = img.width * ratio;
canvas.height = img.height * ratio;
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
canvas.toBlob(resolve, 'image/jpeg', quality);
};
img.src = URL.createObjectURL(file);
});
}
```
## 🔍 测试验证
### 1. 功能测试清单
- [ ] 单张图片上传
- [ ] 多张图片批量上传
- [ ] 图片格式验证
- [ ] 文件大小限制
- [ ] 详情图集解析
- [ ] 详情图集渲染
- [ ] 接口响应验证
### 2. 兼容性测试
- [ ] 现有工作流不受影响
- [ ] 现有课程不受影响
- [ ] API响应格式保持一致
- [ ] 数据库操作正常
## 🆘 常见问题
**Q: 详情图集是必填字段吗?**
A: 不是,详情图集是可选字段,不影响现有功能。
**Q: 如何清空详情图集?**
A: 设置 `detailGallery` 为空字符串 `""``null`
**Q: 支持的最大图片数量?**
A: 理论上无限制但建议2-5张以获得最佳体验。
**Q: 上传失败如何处理?**
A: 实现重试机制,并提供详细的错误信息。
---
**更新时间**: 2024-12-01
**版本**: v1.0