feat: 完成前后端API联调测试并修复配置问题
- 启动后端go-zero API服务 (端口8080) - 修复前端API配置中的端口号 (8888→8080) - 完善前端API状态监控组件 - 创建categoryService服务层 - 更新前端数据查询和转换逻辑 - 完成完整API集成测试,验证所有接口正常工作 - 验证用户认证、分类管理、照片管理等核心功能 - 创建API集成测试脚本 - 更新任务进度文档 测试结果: ✅ 后端健康检查正常 ✅ 用户认证功能正常 (admin/admin123) ✅ 分类API正常 (5个分类) ✅ 照片API正常 (0张照片,数据库为空) ✅ 前后端API连接完全正常 下一步: 实现照片展示页面和搜索过滤功能
This commit is contained in:
@ -1,12 +1,13 @@
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import api from './api'
|
||||
import { categoryService } from './categoryService'
|
||||
|
||||
// 照片数据类型
|
||||
// 照片数据类型 - 统一的显示格式
|
||||
export interface Photo {
|
||||
id: number
|
||||
src: string
|
||||
title: string
|
||||
description: string
|
||||
src: string
|
||||
category: string
|
||||
tags: string[]
|
||||
date: string
|
||||
@ -16,6 +17,38 @@ export interface Photo {
|
||||
settings: string
|
||||
location: string
|
||||
}
|
||||
// 后端原始数据 (仅供内部使用)
|
||||
file_path?: string
|
||||
thumbnail_path?: string
|
||||
user_id?: number
|
||||
category_id?: number
|
||||
created_at?: number
|
||||
updated_at?: number
|
||||
}
|
||||
|
||||
// 分类数据类型
|
||||
export interface Category {
|
||||
id: number
|
||||
name: string
|
||||
description: string
|
||||
created_at: number
|
||||
updated_at: number
|
||||
}
|
||||
|
||||
// 后端分页响应格式
|
||||
export interface PageResponse<T> {
|
||||
total: number
|
||||
page: number
|
||||
size: number
|
||||
photos?: T[]
|
||||
categories?: T[]
|
||||
}
|
||||
|
||||
// 后端API基础响应格式
|
||||
export interface ApiResponse<T> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
// 查询键
|
||||
@ -25,11 +58,77 @@ export const queryKeys = {
|
||||
categories: ['categories'] as const,
|
||||
}
|
||||
|
||||
// 数据转换工具
|
||||
const transformPhoto = async (backendPhoto: any): Promise<Photo> => {
|
||||
// 如果使用Mock API,直接返回
|
||||
if (process.env.NEXT_PUBLIC_USE_REAL_API !== 'true') {
|
||||
return {
|
||||
...backendPhoto,
|
||||
src: backendPhoto.src || '/placeholder.jpg',
|
||||
category: backendPhoto.category || 'general',
|
||||
tags: backendPhoto.tags || [],
|
||||
date: backendPhoto.date || new Date().toISOString().split('T')[0],
|
||||
exif: backendPhoto.exif || {
|
||||
camera: '未知',
|
||||
lens: '未知',
|
||||
settings: '未知',
|
||||
location: '未知'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取分类名称
|
||||
const categoryName = await categoryService.getCategoryName(backendPhoto.category_id)
|
||||
|
||||
// 转换后端API数据格式
|
||||
return {
|
||||
id: backendPhoto.id,
|
||||
title: backendPhoto.title || '无标题',
|
||||
description: backendPhoto.description || '',
|
||||
src: backendPhoto.file_path ? `http://localhost:8080${backendPhoto.file_path}` : '/placeholder.jpg',
|
||||
category: categoryName,
|
||||
tags: [], // 后端暂无标签系统,使用空数组
|
||||
date: new Date(backendPhoto.created_at * 1000).toISOString().split('T')[0],
|
||||
exif: {
|
||||
camera: '未知',
|
||||
lens: '未知',
|
||||
settings: '未知',
|
||||
location: '未知'
|
||||
},
|
||||
// 保留原始数据供内部使用
|
||||
file_path: backendPhoto.file_path,
|
||||
thumbnail_path: backendPhoto.thumbnail_path,
|
||||
user_id: backendPhoto.user_id,
|
||||
category_id: backendPhoto.category_id,
|
||||
created_at: backendPhoto.created_at,
|
||||
updated_at: backendPhoto.updated_at
|
||||
}
|
||||
}
|
||||
|
||||
const transformCategory = (backendCategory: any): string => {
|
||||
if (process.env.NEXT_PUBLIC_USE_REAL_API !== 'true') {
|
||||
return backendCategory
|
||||
}
|
||||
return backendCategory.name
|
||||
}
|
||||
|
||||
// 获取所有照片
|
||||
export const usePhotos = () => {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.photos,
|
||||
queryFn: (): Promise<Photo[]> => api.get('/photos'),
|
||||
queryFn: async (): Promise<Photo[]> => {
|
||||
if (process.env.NEXT_PUBLIC_USE_REAL_API === 'true') {
|
||||
// 使用真实API,带分页参数
|
||||
const response: any = await api.get('/photos?page=1&page_size=100')
|
||||
const photos = response?.photos || []
|
||||
// 并发处理所有照片的转换
|
||||
return Promise.all(photos.map(transformPhoto))
|
||||
} else {
|
||||
// 使用Mock API
|
||||
const photos: any[] = await api.get('/photos')
|
||||
return Promise.all(photos.map(transformPhoto))
|
||||
}
|
||||
},
|
||||
staleTime: 5 * 60 * 1000, // 5分钟内不重新获取
|
||||
})
|
||||
}
|
||||
@ -38,7 +137,14 @@ export const usePhotos = () => {
|
||||
export const usePhoto = (id: number) => {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.photo(id),
|
||||
queryFn: (): Promise<Photo> => api.get(`/photos/${id}`),
|
||||
queryFn: async (): Promise<Photo> => {
|
||||
if (process.env.NEXT_PUBLIC_USE_REAL_API === 'true') {
|
||||
const response = await api.get(`/photos/${id}`)
|
||||
return await transformPhoto(response)
|
||||
} else {
|
||||
return api.get(`/photos/${id}`)
|
||||
}
|
||||
},
|
||||
enabled: !!id,
|
||||
})
|
||||
}
|
||||
@ -47,7 +153,16 @@ export const usePhoto = (id: number) => {
|
||||
export const useCategories = () => {
|
||||
return useQuery({
|
||||
queryKey: queryKeys.categories,
|
||||
queryFn: (): Promise<string[]> => api.get('/categories'),
|
||||
queryFn: async (): Promise<string[]> => {
|
||||
if (process.env.NEXT_PUBLIC_USE_REAL_API === 'true') {
|
||||
const response: any = await api.get('/categories?page=1&page_size=100')
|
||||
const categories = response?.categories || []
|
||||
return categories.map((cat: Category) => cat.name)
|
||||
} else {
|
||||
const categories: string[] = await api.get('/categories')
|
||||
return categories
|
||||
}
|
||||
},
|
||||
staleTime: 10 * 60 * 1000, // 10分钟内不重新获取
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user