723 lines
20 KiB
Markdown
723 lines
20 KiB
Markdown
|
|
# 广场作品TaskId查询功能说明
|
|||
|
|
|
|||
|
|
## 📋 功能概述
|
|||
|
|
|
|||
|
|
新增了通过任务编号(taskId/taskNo)查询作品详情的功能,具有以下特点:
|
|||
|
|
- ✅ 使用taskId查询时**不会增加浏览数**,避免对作品热度数据产生影响
|
|||
|
|
- ✅ **支持查询未发布的任务**,即使任务还没发布到广场也能查看详情
|
|||
|
|
- ✅ 自动大小写转换,兼容大小写输入
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 业务需求
|
|||
|
|
|
|||
|
|
在某些场景下(如用户从自己的任务列表查看作品),需要通过任务编号直接查看详情,包括:
|
|||
|
|
1. **已发布作品**:查看广场作品的完整信息(包含浏览数、点赞数等社交数据)
|
|||
|
|
2. **未发布任务**:查看任务的基本信息(不包含社交数据)
|
|||
|
|
|
|||
|
|
此时不应计入公开浏览数,以保证统计数据的准确性。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔧 实现方案
|
|||
|
|
|
|||
|
|
### 1. 接口修改
|
|||
|
|
|
|||
|
|
**接口路径**: `GET /user/plaza/works/{identifier}`
|
|||
|
|
|
|||
|
|
**新增查询参数**: `queryType`
|
|||
|
|
|
|||
|
|
| 参数名 | 类型 | 必填 | 默认值 | 说明 |
|
|||
|
|
|-------|------|------|--------|------|
|
|||
|
|
| `queryType` | String | 否 | `workNo` | 查询类型:<br>- `workNo`: 通过作品编号查询(记录浏览)<br>- `taskId`: 通过任务编号查询(不记录浏览) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📡 接口使用示例
|
|||
|
|
|
|||
|
|
### 示例1:通过作品编号查询(默认方式,记录浏览)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
curl -X 'GET' \
|
|||
|
|
'http://116.62.4.26:8081/user/plaza/works/WORK-1762440663366-3093' \
|
|||
|
|
-H 'accept: */*'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
或明确指定:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
curl -X 'GET' \
|
|||
|
|
'http://116.62.4.26:8081/user/plaza/works/WORK-1762440663366-3093?queryType=workNo' \
|
|||
|
|
-H 'accept: */*'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**行为**:
|
|||
|
|
- ✅ 返回作品详情
|
|||
|
|
- ✅ 增加浏览数 +1
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 示例2:通过任务编号查询(不记录浏览)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
curl -X 'GET' \
|
|||
|
|
'http://116.62.4.26:8081/user/plaza/works/TASK-20251026183750127-8554?queryType=taskId' \
|
|||
|
|
-H 'accept: */*'
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**行为**:
|
|||
|
|
- ✅ 返回作品详情(已发布)或任务详情(未发布)
|
|||
|
|
- ❌ **不增加浏览数**(保持原有数据)
|
|||
|
|
|
|||
|
|
**返回数据区别**:
|
|||
|
|
- 如果已发布到广场:返回完整作品信息,包含标题、描述、标签、浏览数、点赞数等
|
|||
|
|
- 如果未发布:返回任务基本信息,社交数据(浏览数、点赞数等)为0,标题/描述/标签为空
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔄 业务流程对比
|
|||
|
|
|
|||
|
|
### 通过workNo查询(原有逻辑)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户请求 GET /plaza/works/{workNo}
|
|||
|
|
↓
|
|||
|
|
提取当前用户ID
|
|||
|
|
↓
|
|||
|
|
【记录浏览】增加view_count +1
|
|||
|
|
↓
|
|||
|
|
查询作品详情(联表查询)
|
|||
|
|
↓
|
|||
|
|
检查点赞状态
|
|||
|
|
↓
|
|||
|
|
返回作品详情
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 通过taskId查询(新增逻辑)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
用户请求 GET /plaza/works/{taskNo}?queryType=taskId
|
|||
|
|
↓
|
|||
|
|
提取当前用户ID
|
|||
|
|
↓
|
|||
|
|
【跳过浏览记录】不增加view_count
|
|||
|
|
↓
|
|||
|
|
【步骤1】查询广场作品表(plaza_work)
|
|||
|
|
├── 找到 → 返回完整作品详情(含社交数据)
|
|||
|
|
└── 未找到 → 继续下一步
|
|||
|
|
↓
|
|||
|
|
【步骤2】查询AI任务表(ai_task)
|
|||
|
|
├── 找到 → 返回任务基本信息(社交数据为0)
|
|||
|
|
└── 未找到 → 抛出"任务不存在"异常
|
|||
|
|
↓
|
|||
|
|
返回详情
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💻 代码实现
|
|||
|
|
|
|||
|
|
### 1. 控制器层修改
|
|||
|
|
|
|||
|
|
**文件**: `PlazaController.java`
|
|||
|
|
|
|||
|
|
**修改位置**: `getWorkDetail` 方法
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@GetMapping("/works/{workNo}")
|
|||
|
|
public Result<WorkDetailResponse> getWorkDetail(
|
|||
|
|
@PathVariable String workNo,
|
|||
|
|
@RequestParam(defaultValue = "workNo") String queryType) {
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
Long userId = SecurityUtil.getCurrentUserIdOrNull();
|
|||
|
|
WorkDetailResponse response;
|
|||
|
|
|
|||
|
|
// 根据查询类型处理
|
|||
|
|
if ("taskId".equalsIgnoreCase(queryType)) {
|
|||
|
|
// 通过taskId查询,不记录浏览
|
|||
|
|
log.info("通过taskId查询作品详情,taskNo: {}", workNo);
|
|||
|
|
response = plazaService.getWorkDetailByTaskNo(userId, workNo);
|
|||
|
|
} else {
|
|||
|
|
// 通过workNo查询,记录浏览
|
|||
|
|
log.info("通过workNo查询作品详情,workNo: {}", workNo);
|
|||
|
|
plazaService.recordView(workNo, userId);
|
|||
|
|
response = plazaService.getWorkDetail(userId, workNo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return Result.success(response);
|
|||
|
|
} catch (Exception e) {
|
|||
|
|
log.error("查询作品详情失败,identifier: {}, queryType: {}", workNo, queryType, e);
|
|||
|
|
return Result.error(e.getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**关键逻辑**:
|
|||
|
|
- 通过 `queryType` 参数判断查询类型
|
|||
|
|
- `taskId` 模式:直接调用 `getWorkDetailByTaskNo`,跳过 `recordView`
|
|||
|
|
- `workNo` 模式:保持原有逻辑,先记录浏览再查询
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 2. 服务接口层
|
|||
|
|
|
|||
|
|
**文件**: `PlazaService.java`
|
|||
|
|
|
|||
|
|
**新增方法**:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
/**
|
|||
|
|
* 通过任务编号查询作品详情(不记录浏览)
|
|||
|
|
*
|
|||
|
|
* @param userId 当前用户ID(可为null)
|
|||
|
|
* @param taskNo 任务编号
|
|||
|
|
* @return 作品详情
|
|||
|
|
*/
|
|||
|
|
WorkDetailResponse getWorkDetailByTaskNo(Long userId, String taskNo);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 3. 服务实现层
|
|||
|
|
|
|||
|
|
**文件**: `PlazaServiceImpl.java`
|
|||
|
|
|
|||
|
|
**新增实现**:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@Override
|
|||
|
|
public WorkDetailResponse getWorkDetailByTaskNo(Long userId, String taskNo) {
|
|||
|
|
log.info("通过任务编号查询作品详情(不记录浏览),taskNo: {}", taskNo);
|
|||
|
|
|
|||
|
|
// 1. 统一转换为大写(兼容大小写输入)
|
|||
|
|
String normalizedTaskNo = taskNo.toUpperCase();
|
|||
|
|
|
|||
|
|
// 2. 先查询广场作品(已发布)
|
|||
|
|
Map<String, Object> workMap = plazaWorkMapper.findWorkDetailByTaskNo(normalizedTaskNo);
|
|||
|
|
|
|||
|
|
if (workMap != null) {
|
|||
|
|
// 2.1 找到广场作品,检查是否点赞
|
|||
|
|
boolean isLiked = false;
|
|||
|
|
if (userId != null) {
|
|||
|
|
Long workId = (Long) workMap.get("id");
|
|||
|
|
isLiked = plazaWorkLikeMapper.existsByWorkIdAndUserId(workId, userId) > 0;
|
|||
|
|
}
|
|||
|
|
return convertToDetailResponse(workMap, isLiked);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 广场中没找到,查询AI任务表(未发布的任务)
|
|||
|
|
log.info("广场中未找到作品,尝试从任务表查询 - taskNo: {}", normalizedTaskNo);
|
|||
|
|
Map<String, Object> taskMap = aiTaskMapper.findTaskDetailByTaskNo(normalizedTaskNo);
|
|||
|
|
|
|||
|
|
if (taskMap == null) {
|
|||
|
|
throw new IllegalArgumentException("该任务不存在: " + taskNo);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 从任务数据构建作品详情响应(未发布状态)
|
|||
|
|
return convertTaskToWorkDetail(taskMap);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**特点**:
|
|||
|
|
- ✅ **两级查询**:优先查询广场作品,未找到则查询任务表
|
|||
|
|
- ✅ **大小写兼容**:自动转换为大写,兼容小写输入
|
|||
|
|
- ✅ **完整回退**:未发布的任务也能正常返回
|
|||
|
|
- ✅ **不记录浏览**:保证浏览数不变
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 4. 数据访问层
|
|||
|
|
|
|||
|
|
#### PlazaWorkMapper(查询广场作品)
|
|||
|
|
|
|||
|
|
**文件**: `PlazaWorkMapper.java`
|
|||
|
|
|
|||
|
|
**新增SQL查询**:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
/**
|
|||
|
|
* 通过任务编号查询作品详情(包含作者信息)
|
|||
|
|
*/
|
|||
|
|
@Select("SELECT pw.id, pw.work_no, pw.user_id, pw.task_no, pw.task_type, pw.model_name, " +
|
|||
|
|
" pw.prompt, pw.result_url, pw.image_url, pw.aspect_ratio, pw.title, " +
|
|||
|
|
" pw.description, pw.tags, pw.view_count, pw.like_count, pw.share_count, " +
|
|||
|
|
" pw.comment_count, pw.is_public, pw.status, pw.create_time, pw.update_time, " +
|
|||
|
|
" u.username as nickname, u.avatar_url " +
|
|||
|
|
"FROM plaza_work pw " +
|
|||
|
|
"LEFT JOIN user u ON pw.user_id = u.id " +
|
|||
|
|
"WHERE pw.task_no = #{taskNo} AND pw.is_deleted = 0")
|
|||
|
|
Map<String, Object> findWorkDetailByTaskNo(@Param("taskNo") String taskNo);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### AiTaskMapper(查询AI任务)
|
|||
|
|
|
|||
|
|
**文件**: `AiTaskMapper.java` 和 `AiTaskMapper.xml`
|
|||
|
|
|
|||
|
|
**新增方法**:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
/**
|
|||
|
|
* 通过任务编号查询任务详情(包含作者信息)
|
|||
|
|
*/
|
|||
|
|
java.util.Map<String, Object> findTaskDetailByTaskNo(@Param("taskNo") String taskNo);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**XML实现**:
|
|||
|
|
|
|||
|
|
```xml
|
|||
|
|
<!-- 通过任务编号查询任务详情(包含作者信息) -->
|
|||
|
|
<select id="findTaskDetailByTaskNo" resultType="java.util.HashMap">
|
|||
|
|
SELECT
|
|||
|
|
at.id,
|
|||
|
|
at.task_no,
|
|||
|
|
at.user_id,
|
|||
|
|
at.model_name,
|
|||
|
|
at.task_type,
|
|||
|
|
at.prompt,
|
|||
|
|
at.result_url,
|
|||
|
|
at.image_url,
|
|||
|
|
at.aspect_ratio,
|
|||
|
|
at.status,
|
|||
|
|
at.progress,
|
|||
|
|
at.error_message,
|
|||
|
|
at.create_time,
|
|||
|
|
at.update_time,
|
|||
|
|
u.username as nickname,
|
|||
|
|
u.avatar_url
|
|||
|
|
FROM ai_task at
|
|||
|
|
LEFT JOIN user u ON at.user_id = u.id
|
|||
|
|
WHERE at.task_no = #{taskNo} AND at.is_deleted = 0
|
|||
|
|
</select>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**SQL特点**:
|
|||
|
|
- 查询AI任务表的基本字段
|
|||
|
|
- 通过LEFT JOIN关联用户信息
|
|||
|
|
- 包含任务状态、进度、错误信息等
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 响应数据结构
|
|||
|
|
|
|||
|
|
### 情况1:查询已发布到广场的作品
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": {
|
|||
|
|
"workNo": "WORK-1762440663366-3093",
|
|||
|
|
"taskNo": "TASK-20251026183750127-8554",
|
|||
|
|
"taskType": "text_to_video",
|
|||
|
|
"modelName": "minimax-video-01",
|
|||
|
|
"prompt": "战争场景,恢弘气势...",
|
|||
|
|
"resultUrl": "https://oss.example.com/videos/xxx.mp4",
|
|||
|
|
"imageUrl": "https://oss.example.com/images/ref.jpg",
|
|||
|
|
"aspectRatio": "16:9",
|
|||
|
|
"title": "战场气氛短视频",
|
|||
|
|
"description": "根据参考图生成的恢弘战场场景",
|
|||
|
|
"tags": ["视频", "战争", "特效"],
|
|||
|
|
"viewCount": 125,
|
|||
|
|
"likeCount": 38,
|
|||
|
|
"shareCount": 5,
|
|||
|
|
"isLiked": true,
|
|||
|
|
"author": {
|
|||
|
|
"userId": 17563793187762127,
|
|||
|
|
"nickname": "用户123456",
|
|||
|
|
"avatarUrl": "https://img.remit.ee/api/file/xxx.png"
|
|||
|
|
},
|
|||
|
|
"createTime": "2024-11-06T10:30:00",
|
|||
|
|
"updateTime": "2024-11-07T15:45:00"
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 情况2:查询未发布的任务
|
|||
|
|
|
|||
|
|
#### 2.1 任务已完成
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": {
|
|||
|
|
"workNo": null, // 未发布,无作品编号
|
|||
|
|
"taskNo": "TASK-20251106195754218-9940",
|
|||
|
|
"taskType": "text_to_video",
|
|||
|
|
"modelName": "minimax-video-01",
|
|||
|
|
"prompt": "战争场景,恢弘气势...",
|
|||
|
|
"resultUrl": "https://oss.example.com/videos/xxx.mp4", // ✅ 已完成,有结果
|
|||
|
|
"imageUrl": null, // text_to_video 任务通常无参考图
|
|||
|
|
"aspectRatio": "16:9",
|
|||
|
|
"title": null, // 未发布,无标题
|
|||
|
|
"description": null, // 未发布,无描述
|
|||
|
|
"tags": [], // 未发布,无标签
|
|||
|
|
"viewCount": 0, // 未发布,浏览数为0
|
|||
|
|
"likeCount": 0, // 未发布,点赞数为0
|
|||
|
|
"shareCount": 0, // 未发布,分享数为0
|
|||
|
|
"isLiked": false, // 未发布,不可点赞
|
|||
|
|
"author": {
|
|||
|
|
"userId": 17563793187762127,
|
|||
|
|
"nickname": "用户123456",
|
|||
|
|
"avatarUrl": "https://img.remit.ee/api/file/xxx.png"
|
|||
|
|
},
|
|||
|
|
"createTime": "2024-11-06T19:57:54",
|
|||
|
|
"updateTime": "2024-11-06T20:05:30",
|
|||
|
|
"taskStatus": "completed", // ✅ 任务状态:已完成
|
|||
|
|
"taskProgress": 100, // ✅ 任务进度:100%
|
|||
|
|
"errorMessage": null // ✅ 无错误
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2.2 任务处理中
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": {
|
|||
|
|
"workNo": null,
|
|||
|
|
"taskNo": "TASK-20251106195754218-9940",
|
|||
|
|
"taskType": "text_to_video",
|
|||
|
|
"modelName": "minimax-video-01",
|
|||
|
|
"prompt": "战争场景,恢弘气势...",
|
|||
|
|
"resultUrl": null, // ⏳ 处理中,暂无结果
|
|||
|
|
"imageUrl": null,
|
|||
|
|
"aspectRatio": "16:9",
|
|||
|
|
"title": null,
|
|||
|
|
"description": null,
|
|||
|
|
"tags": [],
|
|||
|
|
"viewCount": 0,
|
|||
|
|
"likeCount": 0,
|
|||
|
|
"shareCount": 0,
|
|||
|
|
"isLiked": false,
|
|||
|
|
"author": {
|
|||
|
|
"userId": 17563793187762127,
|
|||
|
|
"nickname": "用户123456",
|
|||
|
|
"avatarUrl": "https://img.remit.ee/api/file/xxx.png"
|
|||
|
|
},
|
|||
|
|
"createTime": "2024-11-06T19:57:54",
|
|||
|
|
"updateTime": "2024-11-06T19:58:55",
|
|||
|
|
"taskStatus": "processing", // ⏳ 任务状态:处理中
|
|||
|
|
"taskProgress": 45, // ⏳ 任务进度:45%
|
|||
|
|
"errorMessage": null
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 2.3 任务失败
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 200,
|
|||
|
|
"message": "success",
|
|||
|
|
"data": {
|
|||
|
|
"workNo": null,
|
|||
|
|
"taskNo": "TASK-20251106195754218-9940",
|
|||
|
|
"taskType": "text_to_video",
|
|||
|
|
"modelName": "minimax-video-01",
|
|||
|
|
"prompt": "战争场景,恢弘气势...",
|
|||
|
|
"resultUrl": null, // ❌ 失败,无结果
|
|||
|
|
"imageUrl": null,
|
|||
|
|
"aspectRatio": "16:9",
|
|||
|
|
"title": null,
|
|||
|
|
"description": null,
|
|||
|
|
"tags": [],
|
|||
|
|
"viewCount": 0,
|
|||
|
|
"likeCount": 0,
|
|||
|
|
"shareCount": 0,
|
|||
|
|
"isLiked": false,
|
|||
|
|
"author": {
|
|||
|
|
"userId": 17563793187762127,
|
|||
|
|
"nickname": "用户123456",
|
|||
|
|
"avatarUrl": "https://img.remit.ee/api/file/xxx.png"
|
|||
|
|
},
|
|||
|
|
"createTime": "2024-11-06T19:57:54",
|
|||
|
|
"updateTime": "2024-11-06T20:10:30",
|
|||
|
|
"taskStatus": "failed", // ❌ 任务状态:失败
|
|||
|
|
"taskProgress": 0, // ❌ 任务进度:0%
|
|||
|
|
"errorMessage": "生成超时或服务异常" // ❌ 错误信息
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**关键区别**:
|
|||
|
|
|
|||
|
|
| 字段 | 已发布作品 | 未发布任务 |
|
|||
|
|
|-----|-----------|-----------|
|
|||
|
|
| `workNo` | 有值 | `null` |
|
|||
|
|
| `title` | 用户设置的标题 | `null` |
|
|||
|
|
| `description` | 用户设置的描述 | `null` |
|
|||
|
|
| `tags` | 用户设置的标签数组 | `[]` 空数组 |
|
|||
|
|
| `viewCount` | 真实浏览数 | `0` |
|
|||
|
|
| `likeCount` | 真实点赞数 | `0` |
|
|||
|
|
| `shareCount` | 真实分享数 | `0` |
|
|||
|
|
| `isLiked` | 真实点赞状态 | `false` |
|
|||
|
|
| `taskStatus` | `null` | 任务状态(queued/processing/completed/failed) |
|
|||
|
|
| `taskProgress` | `null` | 任务进度(0-100) |
|
|||
|
|
| `errorMessage` | `null` | 错误信息(失败时才有) |
|
|||
|
|
| `resultUrl` | 总是有值 | 仅completed时有值 |
|
|||
|
|
| `imageUrl` | 可能有值 | text_to_video通常为null |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 任务状态说明
|
|||
|
|
|
|||
|
|
### 任务状态(taskStatus)
|
|||
|
|
|
|||
|
|
| 状态 | 说明 | resultUrl | 前端建议 |
|
|||
|
|
|-----|------|-----------|---------|
|
|||
|
|
| `queued` | 排队中 | `null` | 显示"排队中..." |
|
|||
|
|
| `processing` | 处理中 | `null` | 显示进度条(使用taskProgress) |
|
|||
|
|
| `completed` | 已完成 | ✅ 有值 | 显示视频播放器 |
|
|||
|
|
| `failed` | 失败 | `null` | 显示错误提示(使用errorMessage) |
|
|||
|
|
|
|||
|
|
### 为什么resultUrl为null?
|
|||
|
|
|
|||
|
|
1. **任务未完成**:`taskStatus` 为 `queued` 或 `processing`
|
|||
|
|
- 解决方案:轮询查询,等待任务完成
|
|||
|
|
|
|||
|
|
2. **任务失败**:`taskStatus` 为 `failed`
|
|||
|
|
- 解决方案:显示 `errorMessage` 给用户
|
|||
|
|
|
|||
|
|
3. **任务刚完成**:数据库更新延迟
|
|||
|
|
- 解决方案:刷新重试
|
|||
|
|
|
|||
|
|
### 为什么imageUrl为null?
|
|||
|
|
|
|||
|
|
1. **text_to_video任务**:文本生成视频,不需要参考图 ✅ 正常
|
|||
|
|
2. **image_to_video任务**:图生视频,应该有参考图URL
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 使用场景
|
|||
|
|
|
|||
|
|
### 场景1:公开广场浏览(使用workNo)
|
|||
|
|
|
|||
|
|
**场景描述**: 用户在广场浏览其他人的作品
|
|||
|
|
|
|||
|
|
**调用方式**: `GET /plaza/works/{workNo}` 或 `?queryType=workNo`
|
|||
|
|
|
|||
|
|
**预期行为**:
|
|||
|
|
- ✅ 记录浏览数
|
|||
|
|
- ✅ 用于热度排序
|
|||
|
|
- ✅ 统计作品受欢迎程度
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 场景2:用户查看自己的作品(使用taskId)
|
|||
|
|
|
|||
|
|
**场景描述**: 用户从"我的任务"列表点击查看已发布的作品
|
|||
|
|
|
|||
|
|
**调用方式**: `GET /plaza/works/{taskNo}?queryType=taskId`
|
|||
|
|
|
|||
|
|
**预期行为**:
|
|||
|
|
- ❌ 不记录浏览数(避免刷数据)
|
|||
|
|
- ✅ 查看真实的浏览统计
|
|||
|
|
- ✅ 预览作品效果
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 场景3:外部分享链接(使用workNo)
|
|||
|
|
|
|||
|
|
**场景描述**: 通过分享链接访问作品
|
|||
|
|
|
|||
|
|
**调用方式**: `GET /plaza/works/{workNo}`
|
|||
|
|
|
|||
|
|
**预期行为**:
|
|||
|
|
- ✅ 记录浏览数
|
|||
|
|
- ✅ 统计分享传播效果
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔒 安全性考虑
|
|||
|
|
|
|||
|
|
### 1. 权限控制
|
|||
|
|
|
|||
|
|
- ✅ 两种查询方式都支持未登录用户访问
|
|||
|
|
- ✅ 已登录用户可查看个性化点赞状态
|
|||
|
|
- ✅ 查询条件都包含 `is_deleted = 0`,过滤已删除作品
|
|||
|
|
|
|||
|
|
### 2. 数据一致性
|
|||
|
|
|
|||
|
|
- ✅ `task_no` 与 `work_no` 在数据库中都有索引(建议)
|
|||
|
|
- ✅ 一个任务最多对应一个作品(发布时已验证)
|
|||
|
|
- ✅ 查询结果经过软删除过滤
|
|||
|
|
|
|||
|
|
### 3. 异常处理
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
// taskId不存在时的错误提示
|
|||
|
|
throw new IllegalArgumentException("该任务对应的作品不存在: " + taskNo);
|
|||
|
|
|
|||
|
|
// workNo不存在时的错误提示
|
|||
|
|
throw new IllegalArgumentException("作品不存在: " + workNo);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📈 数据库优化建议
|
|||
|
|
|
|||
|
|
### 推荐索引
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- 任务编号索引(如果尚未创建)
|
|||
|
|
CREATE INDEX idx_task_no ON plaza_work(task_no);
|
|||
|
|
|
|||
|
|
-- 作品编号索引(应该已存在)
|
|||
|
|
CREATE UNIQUE INDEX uk_work_no ON plaza_work(work_no);
|
|||
|
|
|
|||
|
|
-- 复合索引(优化查询性能)
|
|||
|
|
CREATE INDEX idx_task_no_deleted ON plaza_work(task_no, is_deleted);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🧪 测试建议
|
|||
|
|
|
|||
|
|
### 测试用例1: 通过workNo查询(记录浏览)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 第一次查询
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/WORK-xxx'
|
|||
|
|
# 预期: viewCount = N
|
|||
|
|
|
|||
|
|
# 第二次查询
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/WORK-xxx'
|
|||
|
|
# 预期: viewCount = N + 1
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 测试用例2: 通过taskId查询(不记录浏览)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查询前记录当前浏览数
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/WORK-xxx'
|
|||
|
|
# 假设 viewCount = 100
|
|||
|
|
|
|||
|
|
# 通过taskId查询多次
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/TASK-xxx?queryType=taskId'
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/TASK-xxx?queryType=taskId'
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/TASK-xxx?queryType=taskId'
|
|||
|
|
|
|||
|
|
# 再次通过workNo查询确认
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/WORK-xxx'
|
|||
|
|
# 预期: viewCount 仍然为 100(未增加)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 测试用例3: 错误的taskId
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/TASK-invalid?queryType=taskId'
|
|||
|
|
|
|||
|
|
# 预期响应:
|
|||
|
|
{
|
|||
|
|
"code": 400,
|
|||
|
|
"message": "该任务对应的作品不存在: TASK-invalid",
|
|||
|
|
"data": null
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### 测试用例4: 未发布作品的taskId
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 对于未发布到广场的任务
|
|||
|
|
curl 'http://116.62.4.26:8081/user/plaza/works/TASK-未发布?queryType=taskId'
|
|||
|
|
|
|||
|
|
# 预期响应:
|
|||
|
|
{
|
|||
|
|
"code": 400,
|
|||
|
|
"message": "该任务对应的作品不存在: TASK-未发布",
|
|||
|
|
"data": null
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 日志记录
|
|||
|
|
|
|||
|
|
### 通过workNo查询的日志
|
|||
|
|
|
|||
|
|
```log
|
|||
|
|
INFO - 通过workNo查询作品详情,workNo: WORK-1762440663366-3093
|
|||
|
|
DEBUG - 记录作品浏览,workNo: WORK-1762440663366-3093, userId: 17563793187762127
|
|||
|
|
INFO - 查询作品详情,workNo: WORK-1762440663366-3093
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 通过taskId查询的日志
|
|||
|
|
|
|||
|
|
```log
|
|||
|
|
INFO - 通过taskId查询作品详情,taskNo: TASK-20251026183750127-8554
|
|||
|
|
INFO - 通过任务编号查询作品详情(不记录浏览),taskNo: TASK-20251026183750127-8554
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 修改文件清单
|
|||
|
|
|
|||
|
|
| 文件路径 | 修改内容 |
|
|||
|
|
|---------|---------|
|
|||
|
|
| `PlazaController.java` | 新增 `queryType` 参数,根据类型分发请求 |
|
|||
|
|
| `PlazaService.java` | 新增 `getWorkDetailByTaskNo()` 接口方法 |
|
|||
|
|
| `PlazaServiceImpl.java` | 实现两级查询逻辑、大小写转换、`convertTaskToWorkDetail()` 转换方法 |
|
|||
|
|
| `PlazaWorkMapper.java` | 新增 `findWorkDetailByTaskNo()` SQL查询 |
|
|||
|
|
| `AiTaskMapper.java` | 新增 `findTaskDetailByTaskNo()` 接口方法 |
|
|||
|
|
| `AiTaskMapper.xml` | 新增 `findTaskDetailByTaskNo` SQL实现(关联user表) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎬 总结
|
|||
|
|
|
|||
|
|
✅ **向后兼容** - 原有接口调用方式不受影响
|
|||
|
|
✅ **灵活查询** - 支持通过作品编号或任务编号查询
|
|||
|
|
✅ **完整回退** - 未发布任务也能正常查询
|
|||
|
|
✅ **大小写兼容** - 自动转换大小写,提升用户体验
|
|||
|
|
✅ **数据准确** - taskId查询不影响浏览统计
|
|||
|
|
✅ **完善日志** - 详细记录查询类型和来源
|
|||
|
|
✅ **异常处理** - 明确的错误提示信息
|
|||
|
|
|
|||
|
|
该功能完美满足了在不同场景下查询作品详情的需求,包括已发布作品和未发布任务,同时保证了统计数据的准确性!
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 Bug修复记录
|
|||
|
|
|
|||
|
|
### Bug 1: 大小写敏感问题
|
|||
|
|
|
|||
|
|
**现象**: 传入小写的taskId(如 `task-xxx`)查询失败
|
|||
|
|
|
|||
|
|
**原因**: 数据库中存储的是大写格式(如 `TASK-xxx`)
|
|||
|
|
|
|||
|
|
**解决**: 在所有查询方法中添加 `.toUpperCase()` 转换
|
|||
|
|
|
|||
|
|
**影响范围**: `getWorkDetail`, `getWorkDetailByTaskNo`, `likeWork`, `unlikeWork`, `deleteWork`, `recordView`
|
|||
|
|
|
|||
|
|
### Bug 2: 未发布任务无法查询
|
|||
|
|
|
|||
|
|
**现象**: 传入未发布任务的taskId返回"作品不存在"
|
|||
|
|
|
|||
|
|
**原因**: 只查询了plaza_work表,未发布的任务不在该表中
|
|||
|
|
|
|||
|
|
**解决**: 实现两级查询,先查plaza_work,未找到再查ai_task表
|
|||
|
|
|
|||
|
|
**新增方法**: `AiTaskMapper.findTaskDetailByTaskNo()`, `PlazaServiceImpl.convertTaskToWorkDetail()`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📞 技术支持
|
|||
|
|
|
|||
|
|
如有问题,请查看日志文件或联系技术团队。
|
|||
|
|
|
|||
|
|
**日志关键字**:
|
|||
|
|
- `通过taskId查询作品详情`
|
|||
|
|
- `通过workNo查询作品详情`
|
|||
|
|
- `该任务对应的作品不存在`
|
|||
|
|
|