refactor: restructure directories to improve module organization Foxel.Models.Request.Picture - Foxel.Models.Request.Tag - Foxel.Models.Request.Auth - Foxel.Models.Request.Picture

This commit is contained in:
ShiYu
2025-05-23 15:07:37 +08:00
parent a03e245d67
commit 0691f1c87d
91 changed files with 30 additions and 30 deletions

108
Web/src/api/AuthContext.tsx Normal file
View File

@@ -0,0 +1,108 @@
import React, { createContext, useState, useEffect, useContext, useCallback } from 'react';
import { getCurrentUser, isAuthenticated, clearAuthData, getStoredUser } from './index';
import type { UserProfile } from './types';
import { UserRole } from './types';
interface AuthContextType {
user: UserProfile | null;
loading: boolean;
authenticated: boolean;
authError: string | null;
logout: () => void;
refreshUser: () => Promise<void>;
hasRole: (requiredRole: UserRole) => boolean;
}
const AuthContext = createContext<AuthContextType>({
user: null,
loading: true,
authenticated: false,
authError: null,
logout: () => {},
refreshUser: async () => {},
hasRole: () => false
});
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [user, setUser] = useState<UserProfile | null>(null);
const [loading, setLoading] = useState(true);
const [authError, setAuthError] = useState<string | null>(null);
const refreshUser = useCallback(async () => {
setLoading(true);
setAuthError(null);
try {
const isLoggedIn = isAuthenticated();
if (isLoggedIn) {
const storedUser = getStoredUser();
if (storedUser) {
setUser(storedUser);
}
const response = await getCurrentUser();
if (response.success && response.data) {
setUser(response.data);
} else if (!storedUser) {
setAuthError(response.message || '获取用户信息失败');
clearAuthData();
setUser(null);
}
} else {
clearAuthData();
setUser(null);
}
} catch (error: any) {
setAuthError(error.message || '获取用户信息时发生错误');
if (!getStoredUser()) {
clearAuthData();
setUser(null);
}
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(() => {
clearAuthData();
setUser(null);
}, []);
const hasRole = useCallback((requiredRole: UserRole): boolean => {
if (!user?.roleName) return false;
// 管理员拥有所有权限
if (user.roleName === UserRole.Administrator) {
return true;
}
// 特定角色检查
return user.roleName === requiredRole;
}, [user]);
useEffect(() => {
refreshUser();
}, [refreshUser]);
const contextValue = {
user,
loading,
authenticated: !!user,
authError,
logout,
refreshUser,
hasRole
};
return (
<AuthContext.Provider value={contextValue}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
export default AuthContext;

117
Web/src/api/albumApi.ts Normal file
View File

@@ -0,0 +1,117 @@
import type {
PaginatedResult,
AlbumResponse,
CreateAlbumRequest,
UpdateAlbumRequest,
AlbumPictureRequest,
AlbumPicturesRequest,
BaseResult
} from './types';
import { fetchApi, BASE_URL } from './fetchClient';
// 获取相册列表
export async function getAlbums(
page: number = 1,
pageSize: number = 10,
userId?: number
): Promise<PaginatedResult<AlbumResponse>> {
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const queryParams = new URLSearchParams();
queryParams.append('page', page.toString());
queryParams.append('pageSize', pageSize.toString());
if (userId) {
queryParams.append('userId', userId.toString());
}
const url = `${BASE_URL}/album/get_albums?${queryParams.toString()}`;
const response = await fetch(url, { headers });
const data = await response.json();
return data as PaginatedResult<AlbumResponse>;
} catch (error) {
console.error('获取相册列表失败:', error);
return {
success: false,
message: '网络请求失败,请检查您的网络连接',
data: [],
page: 1,
pageSize: 10,
totalCount: 0,
totalPages: 0,
code: 500,
};
}
}
// 获取单个相册详情
export async function getAlbumById(id: number): Promise<BaseResult<AlbumResponse>> {
return fetchApi<AlbumResponse>(`/album/get_album/${id}`);
}
// 创建相册
export async function createAlbum(data: CreateAlbumRequest): Promise<BaseResult<AlbumResponse>> {
return fetchApi<AlbumResponse>('/album/create_album', {
method: 'POST',
body: JSON.stringify(data),
});
}
// 更新相册
export async function updateAlbum(data: UpdateAlbumRequest): Promise<BaseResult<AlbumResponse>> {
return fetchApi<AlbumResponse>('/album/update_album', {
method: 'POST',
body: JSON.stringify(data),
});
}
// 删除相册
export async function deleteAlbum(id: number): Promise<BaseResult<boolean>> {
return fetchApi<boolean>('/album/delete_album', {
method: 'POST',
body: JSON.stringify(id),
});
}
// 添加多张图片到相册
export async function addPicturesToAlbum(albumId: number, pictureIds: number[]): Promise<BaseResult<boolean>> {
const data: AlbumPicturesRequest = {
albumId,
pictureIds,
};
return fetchApi<boolean>('/album/add_pictures', {
method: 'POST',
body: JSON.stringify(data),
});
}
// 添加图片到相册
export async function addPictureToAlbum(albumId: number, pictureId: number): Promise<BaseResult<boolean>> {
const data: AlbumPictureRequest = {
albumId,
pictureId,
};
return fetchApi<boolean>('/album/add_picture', {
method: 'POST',
body: JSON.stringify(data),
});
}
// 从相册移除图片
export async function removePictureFromAlbum(albumId: number, pictureId: number): Promise<BaseResult<boolean>> {
const data: AlbumPictureRequest = {
albumId,
pictureId,
};
return fetchApi<boolean>('/album/remove_picture', {
method: 'POST',
body: JSON.stringify(data),
});
}

173
Web/src/api/authApi.ts Normal file
View File

@@ -0,0 +1,173 @@
import {type BaseResult, type AuthResponse, type LoginRequest, type RegisterRequest, type UserProfile, type UpdateUserRequest} from './types';
import {fetchApi, BASE_URL} from './fetchClient';
// 认证数据本地存储键
const TOKEN_KEY = 'token';
const USER_KEY = 'user';
// 用户注册
export async function register(data: RegisterRequest): Promise<BaseResult<AuthResponse>> {
return fetchApi<AuthResponse>('/auth/register', {
method: 'POST',
body: JSON.stringify(data),
});
}
// 用户登录
export async function login(data: LoginRequest): Promise<BaseResult<AuthResponse>> {
const response = await fetchApi<AuthResponse>('/auth/login', {
method: 'POST',
body: JSON.stringify(data),
});
if (response.success && response.data) {
clearAuthData(); // 清除旧的认证数据
console.log('登录成功,保存认证数据:', response.data);
saveAuthData(response.data); // 保存新的认证数据
}
return response;
}
// 获取当前登录用户
export async function getCurrentUser(): Promise<BaseResult<UserProfile>> {
try {
const token = getToken();
if (!token) {
return {
success: false,
message: '用户未登录',
code: 401
};
}
const response = await fetchApi<UserProfile>('/auth/get_current_user');
// 如果成功获取到用户数据,更新本地存储
if (response.success && response.data) {
localStorage.setItem(USER_KEY, JSON.stringify(response.data));
}
return response;
} catch (error: any) {
return {
success: false,
message: `获取用户信息失败: ${error.message}`,
code: 500
};
}
}
// 更新用户信息
export async function updateUserInfo(data: UpdateUserRequest): Promise<BaseResult<UserProfile>> {
try {
const response = await fetchApi<UserProfile>('/auth/update', {
method: 'PUT',
body: JSON.stringify(data),
});
// 如果成功更新用户数据,更新本地存储
if (response.success && response.data) {
const user = getStoredUser();
if (user) {
const updatedUser = {
...user,
...response.data
};
localStorage.setItem(USER_KEY, JSON.stringify(updatedUser));
}
}
return response;
} catch (error: any) {
return {
success: false,
message: `更新用户信息失败: ${error.message}`,
code: 500
};
}
}
// 保存认证数据到本地存储
export const saveAuthData = (authData: AuthResponse): void => {
localStorage.setItem(TOKEN_KEY, authData.token);
if (authData.user) {
localStorage.setItem(USER_KEY, JSON.stringify(authData.user));
}
};
// 清除认证数据
export const clearAuthData = (): void => {
localStorage.removeItem(TOKEN_KEY);
localStorage.removeItem(USER_KEY);
};
// 检查是否已认证
export const isAuthenticated = (): boolean => {
return !!getToken();
};
// 获取存储的用户信息
export const getStoredUser = (): UserProfile | null => {
try {
const userJson = localStorage.getItem(USER_KEY);
if (!userJson) return null;
return JSON.parse(userJson) as UserProfile;
} catch (error) {
return null;
}
};
// 获取存储的令牌
export const getToken = (): string | null => {
return localStorage.getItem(TOKEN_KEY);
};
// 处理GitHub OAuth回调接收token并保存
export async function handleOAuthCallback(): Promise<boolean> {
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
const error = urlParams.get('error');
if (error) return false;
if (token) {
try {
// 保存临时token用于API调用
localStorage.setItem(TOKEN_KEY, token);
// 获取完整的用户信息
const userResponse = await getCurrentUser();
if (userResponse.success && userResponse.data) {
// 构造完整的认证响应并保存
const authResponse: AuthResponse = {
token: token,
user: userResponse.data
};
saveAuthData(authResponse);
// 清除URL中的token参数
const url = new URL(window.location.href);
url.searchParams.delete('token');
window.history.replaceState({}, document.title, url.toString());
return true;
}
return false;
} catch (error) {
console.error('第三方登录处理失败:', error);
clearAuthData(); // 清除可能部分保存的数据
return false;
}
}
return false;
}
export function getGitHubLoginUrl(): string {
return `${BASE_URL}/auth/github/login`;
}

View File

@@ -0,0 +1,17 @@
import { fetchApi } from './fetchClient';
import type { BaseResult, PictureProcessingTask } from './types';
/**
* 获取当前用户的所有处理任务
*/
export const getUserTasks = async (): Promise<BaseResult<PictureProcessingTask[]>> => {
return fetchApi<PictureProcessingTask[]>('/background-tasks/user-tasks');
};
/**
* 获取特定图片的处理状态
* @param pictureId 图片ID
*/
export const getPictureProcessingStatus = async (pictureId: number): Promise<BaseResult<PictureProcessingTask>> => {
return fetchApi<PictureProcessingTask>(`/background-tasks/picture-status/${pictureId}`);
};

74
Web/src/api/configApi.ts Normal file
View File

@@ -0,0 +1,74 @@
import { UserRole, type BaseResult, type ConfigResponse, type SetConfigRequest } from './types';
import { fetchApi } from './fetchClient';
// 获取所有配置
export const getAllConfigs = async (): Promise<BaseResult<ConfigResponse[]>> => {
try {
return await fetchApi<ConfigResponse[]>('/config/get_configs');
} catch (error: any) {
return {
success: false,
message: `获取配置失败: ${error.message}`,
code: 500
};
}
};
// 获取单个配置
export const getConfig = async (key: string): Promise<BaseResult<ConfigResponse>> => {
try {
const queryParams = new URLSearchParams();
queryParams.append('key', key);
return await fetchApi<ConfigResponse>(`/config/get_config?${queryParams.toString()}`);
} catch (error: any) {
return {
success: false,
message: `获取配置失败: ${error.message}`,
code: 500
};
}
};
// 设置配置
export const setConfig = async (config: SetConfigRequest): Promise<BaseResult<ConfigResponse>> => {
try {
return await fetchApi<ConfigResponse>('/config/set_config', {
method: 'POST',
body: JSON.stringify(config),
});
} catch (error: any) {
return {
success: false,
message: `设置配置失败: ${error.message}`,
code: 500
};
}
};
// 删除配置
export const deleteConfig = async (key: string): Promise<BaseResult<boolean>> => {
try {
return await fetchApi<boolean>('/config/delete_config', {
method: 'POST',
body: JSON.stringify(key),
});
} catch (error: any) {
return {
success: false,
message: `删除配置失败: ${error.message}`,
code: 500
};
}
};
// 角色权限检查
export const hasRole = (userRole: string | undefined, requiredRole: UserRole): boolean => {
if (!userRole) return false;
// 如果是管理员,拥有所有权限
if (userRole === UserRole.Administrator) return true;
// 精确匹配角色
return userRole === requiredRole;
};

View File

@@ -0,0 +1,31 @@
import type { BaseResult } from './types';
export const BASE_URL = import.meta.env.PROD ? '/api' : 'http://localhost:5153/api';
export async function fetchApi<T = any>(
url: string,
options: RequestInit = {}
): Promise<BaseResult<T>> {
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...options.headers as Record<string, string>,
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(`${BASE_URL}${url}`, {
...options,
headers,
});
const data = await response.json();
return data as BaseResult<T>;
} catch (error) {
console.error('API请求错误:', error);
return {
success: false,
message: '网络请求失败,请检查您的网络连接',
code: 500,
};
}
}

56
Web/src/api/index.ts Normal file
View File

@@ -0,0 +1,56 @@
// 重新导出类型
export * from './authApi';
export * from './types';
// 导出fetch客户端
export { fetchApi, BASE_URL } from './fetchClient';
// 导出Auth API
export {
register,
login,
getCurrentUser,
updateUserInfo, // 添加导出更新用户信息函数
saveAuthData,
clearAuthData,
isAuthenticated,
getStoredUser
} from './authApi';
// 导出Picture API
export {
getPictures,
favoritePicture,
unfavoritePicture,
getUserFavorites,
uploadPicture,
deleteMultiplePictures, // 添加导出删除图片函数
} from './pictureApi';
// 导出Album API
export {
getAlbums,
getAlbumById,
createAlbum,
updateAlbum,
deleteAlbum,
addPictureToAlbum,
addPicturesToAlbum,
removePictureFromAlbum
} from './albumApi';
// 导出BackgroundTask API
export {
getUserTasks,
getPictureProcessingStatus,
} from './backgroundTaskApi';
// 导出Config API
export {
getAllConfigs,
getConfig,
setConfig,
deleteConfig,
hasRole
} from './configApi';

198
Web/src/api/pictureApi.ts Normal file
View File

@@ -0,0 +1,198 @@
import type { PaginatedResult, PictureResponse, FilteredPicturesRequest, BaseResult } from './types';
import { fetchApi, BASE_URL } from './fetchClient';
// 获取图片列表
export async function getPictures(params: FilteredPicturesRequest = {}): Promise<PaginatedResult<PictureResponse>> {
// 添加调试日志
console.log("Search API 请求参数:", params);
// 构建查询参数
const queryParams = new URLSearchParams();
// 添加所有非空参数
if (params.page) queryParams.append('page', params.page.toString());
if (params.pageSize) queryParams.append('pageSize', params.pageSize.toString());
if (params.searchQuery) queryParams.append('searchQuery', params.searchQuery);
if (params.tags) queryParams.append('tags', params.tags);
if (params.startDate) queryParams.append('startDate', params.startDate);
if (params.endDate) queryParams.append('endDate', params.endDate);
if (params.userId) queryParams.append('userId', params.userId.toString());
if (params.sortBy) queryParams.append('sortBy', params.sortBy);
if (params.onlyWithGps !== undefined) queryParams.append('onlyWithGps', params.onlyWithGps.toString());
if (params.useVectorSearch !== undefined) queryParams.append('useVectorSearch', params.useVectorSearch.toString());
if (params.similarityThreshold) queryParams.append('similarityThreshold', params.similarityThreshold.toString());
if (params.excludeAlbumId) queryParams.append('excludeAlbumId', params.excludeAlbumId.toString());
if (params.albumId) queryParams.append('albumId', params.albumId.toString());
if (params.onlyFavorites !== undefined) queryParams.append('onlyFavorites', params.onlyFavorites.toString());
if (params.ownerId !== undefined) queryParams.append('ownerId', params.ownerId.toString());
if (params.includeAllPublic !== undefined) queryParams.append('includeAllPublic', params.includeAllPublic.toString());
// 最终URL调试日志
const url = `${BASE_URL}/picture/get_pictures?${queryParams.toString()}`;
console.log("发送API请求:", url);
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const response = await fetch(url, { headers });
const data = await response.json();
// 添加结果日志
console.log("API 响应结果:", {
success: data.success,
totalCount: data.totalCount,
resultsCount: data.data?.length || 0
});
return data as PaginatedResult<PictureResponse>;
} catch (error) {
console.error('获取图片列表失败:', error);
return {
success: false,
message: '网络请求失败,请检查您的网络连接',
data: [],
page: 1,
pageSize: 10,
totalCount: 0,
totalPages: 0,
code: 500,
};
}
}
// 收藏图片
export async function favoritePicture(pictureId: number): Promise<BaseResult<boolean>> {
return fetchApi<boolean>('/picture/favorite', {
method: 'POST',
body: JSON.stringify({ pictureId }),
});
}
// 取消收藏图片
export async function unfavoritePicture(pictureId: number): Promise<BaseResult<boolean>> {
return fetchApi<boolean>('/picture/unfavorite', {
method: 'POST',
body: JSON.stringify({ pictureId }),
});
}
// 获取用户收藏的图片
export async function getUserFavorites(page: number = 1, pageSize: number = 8): Promise<PaginatedResult<PictureResponse>> {
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const url = `${BASE_URL}/picture/favorites?page=${page}&pageSize=${pageSize}`;
const response = await fetch(url, { headers });
const data = await response.json();
return data as PaginatedResult<PictureResponse>;
} catch (error) {
console.error('获取收藏图片失败:', error);
return {
success: false,
message: '网络请求失败,请检查您的网络连接',
data: [],
page: 1,
pageSize: 10,
totalCount: 0,
totalPages: 0,
code: 500,
};
}
}
// 上传图片
export async function uploadPicture(
file: File,
data: {
permission?: number;
albumId?: number;
onProgress?: (percent: number) => void
} = {}
): Promise<BaseResult<PictureResponse>> {
const formData = new FormData();
formData.append('file', file);
if (data.permission !== undefined) {
formData.append('permission', data.permission.toString());
}
if (data.albumId !== undefined) {
formData.append('albumId', data.albumId.toString());
}
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const xhr = new XMLHttpRequest();
// 返回一个Promise
return new Promise((resolve, reject) => {
xhr.open('POST', `${BASE_URL}/picture/upload_picture`);
if (token) {
xhr.setRequestHeader('Authorization', `Bearer ${token}`);
}
xhr.upload.onprogress = (event) => {
if (event.lengthComputable && data.onProgress) {
const percent = Math.round((event.loaded / event.total) * 100);
data.onProgress(percent);
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
const response = JSON.parse(xhr.responseText);
resolve(response);
} else {
reject({
status: xhr.status,
message: xhr.statusText || '上传失败',
});
}
};
xhr.onerror = () => {
reject({
status: xhr.status,
message: '网络错误,上传失败',
});
};
xhr.send(formData);
});
} catch (error) {
console.error('上传图片失败:', error);
return {
success: false,
message: '上传图片失败',
code: 500,
};
}
}
// 删除多张图片
export async function deleteMultiplePictures(pictureIds: number[]): Promise<BaseResult<object>> {
return fetchApi<object>('/picture/delete_pictures', {
method: 'POST',
body: JSON.stringify({ pictureIds }),
});
}

111
Web/src/api/tagApi.ts Normal file
View File

@@ -0,0 +1,111 @@
import type { BaseResult, PaginatedResult } from './types';
import { fetchApi, BASE_URL } from './fetchClient';
// 标签响应类型
export interface TagResponse {
id: number;
name: string;
description?: string;
pictureCount: number;
createdAt: Date;
}
// 筛选标签请求参数
export interface FilteredTagsRequest {
page?: number;
pageSize?: number;
searchQuery?: string;
sortBy?: string;
sortDirection?: string;
}
// 创建标签请求
export interface CreateTagRequest {
name: string;
description?: string;
}
// 更新标签请求
export interface UpdateTagRequest {
id: number;
name: string;
description?: string;
}
// 获取所有标签
export async function getAllTags(): Promise<BaseResult<TagResponse[]>> {
return fetchApi<TagResponse[]>('/tag/all', {
method: 'GET',
});
}
// 获取筛选后的标签
export async function getFilteredTags(params: FilteredTagsRequest = {}): Promise<PaginatedResult<TagResponse>> {
const queryParams = new URLSearchParams();
if (params.page) queryParams.append('page', params.page.toString());
if (params.pageSize) queryParams.append('pageSize', params.pageSize.toString());
if (params.searchQuery) queryParams.append('searchQuery', params.searchQuery);
if (params.sortBy) queryParams.append('sortBy', params.sortBy);
if (params.sortDirection) queryParams.append('sortDirection', params.sortDirection);
try {
const token = localStorage.getItem('token');
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
const url = `${BASE_URL}/tag/get_tags?${queryParams.toString()}`;
const response = await fetch(url, { headers });
const data = await response.json();
return data as PaginatedResult<TagResponse>;
} catch (error) {
console.error('获取标签列表失败:', error);
return {
success: false,
message: '网络请求失败,请检查您的网络连接',
data: [],
page: 1,
pageSize: 10,
totalCount: 0,
totalPages: 0,
code: 500,
};
}
}
// 获取标签详情
export async function getTagById(id: number): Promise<BaseResult<TagResponse>> {
return fetchApi<TagResponse>(`/tag/${id}`, {
method: 'GET',
});
}
// 创建标签
export async function createTag(request: CreateTagRequest): Promise<BaseResult<TagResponse>> {
return fetchApi<TagResponse>('/tag/create_tag', {
method: 'POST',
body: JSON.stringify(request),
});
}
// 更新标签
export async function updateTag(request: UpdateTagRequest): Promise<BaseResult<TagResponse>> {
return fetchApi<TagResponse>('/tag/update_tag', {
method: 'POST',
body: JSON.stringify(request),
});
}
// 删除标签
export async function deleteTag(id: number): Promise<BaseResult<boolean>> {
return fetchApi<boolean>('/tag/delete_tag', {
method: 'POST',
body: JSON.stringify(id),
});
}

207
Web/src/api/types.ts Normal file
View File

@@ -0,0 +1,207 @@
// API响应的基础结构
export interface BaseResult<T> {
success: boolean;
message: string;
data?: T;
code: number;
}
// 分页结果通用结构
export interface PaginatedResult<T> {
success: boolean;
message: string;
data: T[];
page: number;
pageSize: number;
totalCount: number;
totalPages: number;
code: number;
}
// 登录请求参数
export interface LoginRequest {
email: string;
password: string;
}
// 注册请求参数
export interface RegisterRequest {
username: string;
email: string;
password: string;
}
// 用户信息
export interface UserProfile {
id: number;
userName: string;
email: string;
roleName: string;
}
// 认证响应
export interface AuthResponse {
token: string;
user: UserProfile;
}
// 图片请求参数
export interface FilteredPicturesRequest {
page?: number;
pageSize?: number;
searchQuery?: string;
tags?: string;
startDate?: string;
endDate?: string;
userId?: number;
sortBy?: string;
onlyWithGps?: boolean;
useVectorSearch?: boolean;
similarityThreshold?: number;
excludeAlbumId?: number;
albumId?: number;
onlyFavorites?: boolean;
ownerId?: number;
includeAllPublic?: boolean;
}
// 图片响应数据
export interface PictureResponse {
id: number;
name: string;
path: string;
thumbnailPath: string;
description: string;
takenAt?: Date;
createdAt: Date;
exifInfo?: any;
tags?: string[];
userId: number;
username?: string;
isFavorited: boolean;
favoriteCount: number;
permission: number;
albumId?: number;
albumName?: string;
processingStatus: ProcessingStatus;
processingError?: string;
processingProgress: number;
}
// 收藏请求
export interface FavoriteRequest {
pictureId: number;
}
// 上传队列中的文件项
export interface UploadFile {
id: string; // 本地ID用于跟踪状态
file: File; // 原始文件
status: 'pending' | 'uploading' | 'success' | 'error'; // 上传状态
percent: number; // 上传进度百分比 0-100
error?: string; // 错误信息
response?: PictureResponse; // 上传成功后的响应
}
// 上传图片请求参数
export interface UploadPictureParams {
permission?: number; // 权限设置默认为0公开
albumId?: number; // 相册ID可选
}
// 相册响应数据
export interface AlbumResponse {
id: number;
name: string;
description: string;
coverImageUrl?: string;
pictureCount: number;
userId: number;
username?: string;
createdAt: Date;
updatedAt: Date;
}
// 创建相册请求
export interface CreateAlbumRequest {
name: string;
description: string;
}
// 更新相册请求
export interface UpdateAlbumRequest {
id: number;
name: string;
description: string;
}
// 相册图片操作请求
export interface AlbumPictureRequest {
albumId: number;
pictureId: number;
}
// 批量添加图片到相册请求
export interface AlbumPicturesRequest {
albumId: number;
pictureIds: number[];
}
// 删除多张图片请求
export interface DeleteMultiplePicturesRequest {
pictureIds: number[];
}
// 将类型定义改为枚举,这样既可以作为类型也可以作为值使用
export type ProcessingStatus = 'Pending' | 'Processing' | 'Completed' | 'Failed';
// 添加常量对象提供运行时值
export const ProcessingStatus = {
Pending: 'Pending' as ProcessingStatus,
Processing: 'Processing' as ProcessingStatus,
Completed: 'Completed' as ProcessingStatus,
Failed: 'Failed' as ProcessingStatus
};
// 图片处理任务
export interface PictureProcessingTask {
pictureId: number;
taskId: string;
pictureName: string;
status: ProcessingStatus;
progress: number; // 0-100
error?: string;
createdAt: Date;
completedAt?: Date;
}
// 配置响应数据
export interface ConfigResponse {
id: number;
key: string;
value: string;
description: string;
createdAt: Date;
updatedAt?: Date;
}
export interface SetConfigRequest {
key: string;
value: string;
description?: string;
}
export type UserRole = "Administrator" | "User" | "";
export const UserRole = {
Administrator: "Administrator" as UserRole,
User: "User" as UserRole,
Guest: "" as UserRole
};
export interface UpdateUserRequest {
userName?: string;
email?: string;
currentPassword?: string;
newPassword?: string;
}