# 广场功能完整实现方案 ## 一、功能概述 用户可以将AI生成的作品发布到广场,其他用户可以浏览、点赞、按类型筛选查询作品。 ### 核心功能 1. **发布作品** - 用户将任务结果发布到广场 2. **浏览广场** - 按类型、热度、时间查询作品 3. **作品详情** - 查看单个作品详细信息 4. **点赞/取消点赞** - 用户可以对作品点赞 5. **统计数据** - 浏览量、点赞数、分享数统计 6. **个人作品** - 查看自己发布的作品 7. **删除作品** - 用户可以删除自己的作品 --- ## 二、数据库设计 已在 `V10__add_plaza_feature.sql` 中定义了以下表: ### 1. plaza_work(广场作品表) ```sql - id: 主键 - work_no: 作品编号(唯一) - user_id: 发布者ID - task_no: 关联任务编号 - task_type: 任务类型(text_to_image/image_to_video等) - model_name: 使用的模型 - prompt: 生成提示词 - result_url: 作品URL - image_url: 参考图URL(可选) - title: 作品标题 - description: 作品描述 - tags: 标签(JSON) - view_count, like_count, share_count: 统计数据 - is_public: 是否公开 - status: 状态(draft/published/hidden) - create_time, update_time, is_deleted ``` ### 2. plaza_work_like(点赞表) ```sql - id: 主键 - work_id: 作品ID - user_id: 点赞用户ID - create_time: 点赞时间 - 唯一索引:(work_id, user_id) ``` ### 3. plaza_work_view(浏览记录表) ```sql - id: 主键 - work_id: 作品ID - user_id: 浏览用户ID(可空) - ip_address: IP地址 - view_time: 浏览时间 ``` --- ## 三、API 接口设计 ### 基础路径 所有接口以 `/user/plaza` 开头 ### 1. 发布作品 ``` POST /user/plaza/works/publish ``` **请求参数:** ```json { "taskNo": "TASK-20251026183750127-8554", "title": "战场气氛短视频", "description": "根据参考图生成的恢弘战场场景", "tags": ["视频", "战争", "特效"], "isPublic": true } ``` **响应示例:** ```json { "code": 200, "message": "success", "data": { "workNo": "WORK-20251026185630123-4567", "taskNo": "TASK-20251026183750127-8554", "taskType": "image_to_video", "modelName": "sc_sora2_img_landscape_15s_small", "prompt": "根据参考图展现出一个100秒左右的战场短视频...", "resultUrl": "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com/result.mp4", "imageUrl": "https://oss-1818ai-user-img.oss-cn-hangzhou.aliyuncs.com/ref.png", "title": "战场气氛短视频", "description": "根据参考图生成的恢弘战场场景", "tags": ["视频", "战争", "特效"], "viewCount": 0, "likeCount": 0, "isLiked": false, "createTime": "2025-10-26T18:56:30" } } ``` --- ### 2. 查询广场作品列表 ``` GET /user/plaza/works/list ``` **请求参数:** - `page`: 页码(默认1) - `size`: 每页数量(默认20) - `taskType`: 任务类型筛选(可选:text_to_image/image_to_video等) - `sortBy`: 排序方式(可选:latest-最新/hot-最热,默认latest) **示例:** ``` GET /user/plaza/works/list?page=1&size=20&taskType=text_to_image&sortBy=hot ``` **响应示例:** ```json { "code": 200, "message": "success", "data": { "total": 156, "list": [ { "workNo": "WORK-20251026185630123-4567", "taskType": "text_to_image", "modelName": "sc_soraimg_text_1x1", "prompt": "一只可爱的橘猫在窗台晒太阳", "resultUrl": "https://oss-.../cat.png", "title": "窗台上的橘猫", "tags": ["猫咪", "温馨", "治愈"], "viewCount": 1245, "likeCount": 89, "isLiked": false, "author": { "userId": 17563793187762127, "nickname": "AI创作者", "avatarUrl": "https://oss-.../avatar.jpg" }, "createTime": "2025-10-26T18:56:30" } // ... 更多作品 ] } } ``` --- ### 3. 查询作品详情 ``` GET /user/plaza/works/{workNo} ``` **响应示例:** ```json { "code": 200, "message": "success", "data": { "workNo": "WORK-20251026185630123-4567", "taskType": "text_to_image", "modelName": "sc_soraimg_text_1x1", "prompt": "一只可爱的橘猫在窗台晒太阳,温馨的室内场景,柔和的光线", "resultUrl": "https://oss-.../cat.png", "imageUrl": null, "aspectRatio": "1:1", "title": "窗台上的橘猫", "description": "这是一个温馨治愈的作品,展现了橘猫慵懒晒太阳的画面。", "tags": ["猫咪", "温馨", "治愈"], "viewCount": 1246, "likeCount": 89, "shareCount": 12, "isLiked": false, "author": { "userId": 17563793187762127, "nickname": "AI创作者", "avatarUrl": "https://oss-.../avatar.jpg" }, "createTime": "2025-10-26T18:56:30", "updateTime": "2025-10-26T19:15:42" } } ``` --- ### 4. 点赞/取消点赞 ``` POST /user/plaza/works/{workNo}/like DELETE /user/plaza/works/{workNo}/like ``` **响应示例:** ```json { "code": 200, "message": "success", "data": { "isLiked": true, "likeCount": 90 } } ``` --- ### 5. 查询我的作品 ``` GET /user/plaza/my-works ``` **请求参数:** - `page`: 页码 - `size`: 每页数量 - `status`: 状态筛选(可选:published/draft/hidden) **响应格式:** 同广场作品列表 --- ### 6. 删除作品 ``` DELETE /user/plaza/works/{workNo} ``` **响应示例:** ```json { "code": 200, "message": "删除成功", "data": null } ``` --- ### 7. 统计数据 ``` GET /user/plaza/stats ``` **响应示例:** ```json { "code": 200, "message": "success", "data": { "totalWorks": 5678, "totalViews": 123456, "totalLikes": 8901, "worksByType": { "text_to_image": 3456, "image_to_video": 1234, "text_to_video": 988 } } } ``` --- ## 四、实现步骤 ### 步骤1: 执行SQL脚本 ```bash mysql -u root -p 1818ai < V10__add_plaza_feature.sql ``` ### 步骤2: 创建实体类 - ✅ `PlazaWork.java` - 已创建 - ✅ `PlazaWorkLike.java` - 已创建 ### 步骤3: 创建DTO类 需要创建以下DTO: - `PlazaWorkDto.java` - 包含各种请求和响应DTO - `PublishWorkRequest` - 发布作品请求 - `WorkQueryRequest` - 查询作品请求 - `WorkDetailResponse` - 作品详情响应 - `WorkListResponse` - 作品列表响应 - `WorkAuthorDto` - 作者信息DTO ### 步骤4: 创建Mapper接口 - `PlazaWorkMapper.java` - 作品数据访问 - `PlazaWorkLikeMapper.java` - 点赞数据访问 ### 步骤5: 创建Service层 - `PlazaService.java` - 接口 - `PlazaServiceImpl.java` - 实现 ### 步骤6: 创建Controller - `PlazaController.java` - 用户端控制器 --- ## 五、核心业务逻辑 ### 1. 发布作品流程 ``` 1. 验证taskNo是否存在且属于当前用户 2. 验证任务状态是否为completed 3. 检查该任务是否已经发布过 4. 生成唯一的workNo 5. 从任务表复制数据到广场作品表 6. 设置标题、描述、标签等 7. 初始化统计数据(浏览、点赞等为0) 8. 返回作品信息 ``` ### 2. 查询列表流程 ``` 1. 构建查询条件(任务类型、公开性、状态) 2. 根据sortBy排序(latest/hot) 3. 分页查询 4. 关联查询用户信息(昵称、头像) 5. 判断当前用户是否点赞过(如果已登录) 6. 返回作品列表 ``` ### 3. 点赞流程 ``` 1. 检查作品是否存在 2. 检查是否已经点赞 3. 插入点赞记录 4. 更新作品点赞数(+1) 5. 返回最新点赞状态 ``` ### 4. 浏览统计流程 ``` 1. 记录浏览日志(可选) 2. 更新作品浏览数(+1) 3. 防止频繁刷新(可加Redis缓存,同一用户1分钟内只计数一次) ``` --- ## 六、前端调用示例 ### 1. 发布作品到广场 ```javascript // 假设用户刚完成了一个任务,想发布到广场 async function publishToPlaza(taskNo) { const response = await fetch('http://localhost:8081/user/plaza/works/publish', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${YOUR_TOKEN}` }, body: JSON.stringify({ taskNo: taskNo, title: '我的AI创作', description: '这是我用AI生成的作品', tags: ['AI艺术', '创意'], isPublic: true }) }); const result = await response.json(); console.log('发布成功:', result.data.workNo); } ``` ### 2. 浏览广场(按类型筛选) ```javascript // 查询所有文生图作品 async function browsePlaza() { const response = await fetch( 'http://localhost:8081/user/plaza/works/list?page=1&size=20&taskType=text_to_image&sortBy=hot', { headers: { 'Authorization': `Bearer ${YOUR_TOKEN}` } } ); const result = await response.json(); console.log('广场作品:', result.data.list); // 渲染作品网格 renderWorksGrid(result.data.list); } function renderWorksGrid(works) { works.forEach(work => { console.log(` 作品:${work.title} 作者:${work.author.nickname} 点赞:${work.likeCount} 浏览:${work.viewCount} 图片:${work.resultUrl} `); }); } ``` ### 3. 查看作品详情 ```javascript async function viewWorkDetail(workNo) { const response = await fetch( `http://localhost:8081/user/plaza/works/${workNo}`, { headers: { 'Authorization': `Bearer ${YOUR_TOKEN}` } } ); const result = await response.json(); console.log('作品详情:', result.data); // 显示详情页 showDetailModal(result.data); } ``` ### 4. 点赞作品 ```javascript async function likeWork(workNo) { const response = await fetch( `http://localhost:8081/user/plaza/works/${workNo}/like`, { method: 'POST', headers: { 'Authorization': `Bearer ${YOUR_TOKEN}` } } ); const result = await response.json(); console.log('点赞成功,当前点赞数:', result.data.likeCount); // 更新UI updateLikeButton(workNo, result.data.isLiked, result.data.likeCount); } async function unlikeWork(workNo) { const response = await fetch( `http://localhost:8081/user/plaza/works/${workNo}/like`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${YOUR_TOKEN}` } } ); const result = await response.json(); console.log('取消点赞'); } ``` ### 5. 查看我的作品 ```javascript async function getMyWorks() { const response = await fetch( 'http://localhost:8081/user/plaza/my-works?page=1&size=10', { headers: { 'Authorization': `Bearer ${YOUR_TOKEN}` } } ); const result = await response.json(); console.log('我的作品:', result.data.list); } ``` --- ## 七、React 完整示例 ```jsx import React, { useState, useEffect } from 'react'; // 广场组件 function PlazaPage() { const [works, setWorks] = useState([]); const [taskType, setTaskType] = useState(''); const [sortBy, setSortBy] = useState('latest'); const [page, setPage] = useState(1); useEffect(() => { loadWorks(); }, [taskType, sortBy, page]); async function loadWorks() { const params = new URLSearchParams({ page, size: 20, sortBy, ...(taskType && { taskType }) }); const response = await fetch( `http://localhost:8081/user/plaza/works/list?${params}`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } } ); const result = await response.json(); setWorks(result.data.list); } async function handleLike(workNo, isLiked) { const method = isLiked ? 'DELETE' : 'POST'; const response = await fetch( `http://localhost:8081/user/plaza/works/${workNo}/like`, { method, headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } } ); if (response.ok) { // 刷新列表 loadWorks(); } } return (
{work.author.nickname}