You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8.6 KiB
8.6 KiB
Changelog: 项目 API 对接
版本: v1.1.0
发布日期: 2026-01-21
类型: Feature
影响范围: 前端 API 层、Hooks、类型定义
概述
完成前端项目模块与后端 API 的对接,包括项目 CRUD、回收站功能、分享管理等。
变更内容
1. 类型定义更新
文件: client/src/types/project.ts
新增类型:
MoveProjectDto- 移动项目参数ProjectListParams- 列表查询参数ProjectListResponse- 列表响应TrashProject- 回收站项目TrashListResponse- 回收站列表响应RestoreProjectResponse- 恢复响应
更新类型:
Project- 添加displayOrder,trashedAt字段ProjectStatus- 添加trashed,soft_deleted状态CreateProjectDto- 添加影视项目元数据字段UpdateProjectDto- 添加影视项目元数据字段
2. API 服务实现
文件: client/src/services/api/projects.ts (新建)
实现功能:
基础 CRUD
getAll(params)- 获取项目列表(支持筛选、搜索、排序、分页)getById(id)- 获取单个项目create(data)- 创建项目update(id, data)- 更新项目delete(id)- 删除项目(移至回收站)move(id, data)- 移动项目到文件夹clone(id, data)- 克隆项目updateOrder(id, displayOrder)- 更新项目顺序
回收站功能
getTrash(params)- 获取回收站列表restore(id)- 从回收站恢复permanentDelete(id)- 永久删除
分享管理
createShare(id, data)- 创建分享getShares(id)- 获取分享列表revokeShare(id, shareId)- 撤销分享
成员管理
getMembers(id, params)- 获取成员列表addMember(id, data)- 添加成员removeMember(id, userId)- 移除成员
3. React Query Hooks
文件: client/src/hooks/api/useProjects.ts
新增 Hooks:
useMoveProject()- 移动项目useCloneProject()- 克隆项目useUpdateProjectOrder()- 更新顺序useTrashProjects(params)- 获取回收站列表useRestoreProject()- 恢复项目usePermanentDeleteProject()- 永久删除
更新 Hooks:
useProjects(params)- 支持查询参数useDeleteProject()- 改为移至回收站,失效回收站缓存
Query Keys 更新:
projectKeys = {
all: ['projects'],
lists: () => ['projects', 'list'],
list: (filters) => ['projects', 'list', filters],
details: () => ['projects', 'detail'],
detail: (id) => ['projects', 'detail', id],
trash: () => ['projects', 'trash'],
trashList: (params) => ['projects', 'trash', params],
}
4. API 导出更新
文件: client/src/services/api/index.ts
变更:
- 从 Mock API 切换到真实 API
projectApi现在导出自./projects而非../mock
API 对接说明
请求格式
所有请求使用 camelCase 命名:
{
folderId: "xxx",
contentType: "movie",
sortBy: "updatedAt"
}
响应格式
后端返回 camelCase 格式(通过 alias 配置):
{
id: "xxx",
name: "项目名称",
folderId: "xxx",
createdAt: "2026-01-21T10:00:00Z"
}
分页参数
{
page: 1, // 页码(从 1 开始)
pageSize: 20 // 每页数量
}
分页响应
{
items: [...],
total: 100,
page: 1,
pageSize: 20,
totalPages: 5
}
使用示例
1. 获取项目列表
import { useProjects } from '@/hooks/api';
function ProjectList() {
const { data, isLoading } = useProjects({
folderId: 'folder-123',
sortBy: 'updatedAt',
sortOrder: 'desc',
page: 1,
pageSize: 20
});
if (isLoading) return <div>加载中...</div>;
return (
<div>
{data?.items.map(project => (
<div key={project.id}>{project.name}</div>
))}
<div>共 {data?.total} 个项目</div>
</div>
);
}
2. 创建项目
import { useCreateProject } from '@/hooks/api';
function CreateProjectButton() {
const createProject = useCreateProject();
const handleCreate = () => {
createProject.mutate({
name: '新项目',
description: '项目描述',
type: 'mine',
folderId: 'folder-123',
contentType: 'movie',
aspectRatio: '16:9'
}, {
onSuccess: (project) => {
console.log('创建成功:', project);
}
});
};
return (
<button onClick={handleCreate} disabled={createProject.isPending}>
{createProject.isPending ? '创建中...' : '创建项目'}
</button>
);
}
3. 删除项目(移至回收站)
import { useDeleteProject } from '@/hooks/api';
import { useToast } from '@/hooks/use-toast';
function DeleteProjectButton({ projectId }: { projectId: string }) {
const deleteProject = useDeleteProject();
const { toast } = useToast();
const handleDelete = () => {
deleteProject.mutate(projectId, {
onSuccess: () => {
toast({
title: '已移至回收站',
description: '项目将在 30 天后自动删除'
});
}
});
};
return (
<button onClick={handleDelete}>删除项目</button>
);
}
4. 回收站列表
import { useTrashProjects, useRestoreProject, usePermanentDeleteProject } from '@/hooks/api';
function TrashPage() {
const { data } = useTrashProjects({ page: 1, pageSize: 20 });
const restore = useRestoreProject();
const permanentDelete = usePermanentDeleteProject();
return (
<div>
<h1>回收站</h1>
{data?.items.map(project => (
<div key={project.id}>
<span>{project.name}</span>
<span>{project.daysRemaining} 天后自动删除</span>
<button onClick={() => restore.mutate(project.id)}>
恢复
</button>
<button onClick={() => permanentDelete.mutate(project.id)}>
永久删除
</button>
</div>
))}
</div>
);
}
5. 移动项目
import { useMoveProject } from '@/hooks/api';
function MoveProjectButton({ projectId }: { projectId: string }) {
const moveProject = useMoveProject();
const handleMove = (targetFolderId: string) => {
moveProject.mutate({
id: projectId,
data: { folderId: targetFolderId }
});
};
return <button onClick={() => handleMove('folder-456')}>移动</button>;
}
Breaking Changes
⚠️ API 切换
影响: 所有使用 projectApi 的代码
变更前: Mock API(内存数据)
import { projectApi } from '@/services/mock';
变更后: 真实 API(后端对接)
import { projectApi } from '@/services/api';
迁移方案:
- 代码无需修改(导出路径已更新)
- 确保后端服务已启动
- 确保 API 基础 URL 配置正确
⚠️ 响应格式变更
影响: 项目列表查询
变更前: 返回数组
const projects: Project[] = await projectApi.getAll();
变更后: 返回分页对象
const response: ProjectListResponse = await projectApi.getAll();
const projects = response.items;
迁移方案:
- 使用
useProjects()hook 自动处理 - 直接调用 API 需要访问
.items属性
测试清单
API 测试
- 获取项目列表(无参数)
- 获取项目列表(带筛选)
- 获取项目列表(带搜索)
- 获取项目列表(带排序)
- 获取项目列表(带分页)
- 获取单个项目
- 创建项目
- 更新项目
- 删除项目(移至回收站)
- 移动项目
- 克隆项目
- 更新项目顺序
回收站测试
- 获取回收站列表
- 从回收站恢复
- 永久删除
- 剩余天数显示正确
Hooks 测试
- useProjects 缓存正常
- useProject 缓存正常
- useCreateProject 失效缓存
- useUpdateProject 更新缓存
- useDeleteProject 失效缓存
- useTrashProjects 独立缓存
已知问题
无
后续工作
- 回收站页面 - 实现完整的回收站 UI
- 项目分享 - 实现分享功能 UI
- 成员管理 - 实现协作项目成员管理 UI
- 批量操作 - 支持批量删除、移动等
- 离线支持 - 添加离线缓存策略
相关文档
变更作者: System
审核人: 待定
发布状态: 已完成