From c1a88ea4da570b6f3f8e5cd87f60fa42d578a80b Mon Sep 17 00:00:00 2001 From: jianghao <332515344@qq.com> Date: Fri, 27 Feb 2026 10:07:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor(api):=20=E7=BB=9F=E4=B8=80API=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E9=85=8D=E7=BD=AE=E5=B9=B6=E4=BC=98=E5=8C=96=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=9C=8D=E5=8A=A1=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit feat: 添加API URL全局配置文件 fix: 修复SSR环境下的窗口对象检查 perf: 优化代理配置路径匹配顺序 style: 移除无用注释和未使用的类型声明 --- config/config.ts | 98 ++++++------- config/proxy.ts | 16 ++- src/app.tsx | 52 +++---- src/components/Comments/CommentList.tsx | 3 + src/hooks/useNotifications.ts | 14 +- src/hooks/useReactions.ts | 2 +- src/models/sync.ts | 4 +- src/pages/account/center/service.ts | 17 +-- src/pages/account/settings/service.ts | 11 +- src/pages/albums/components/PhotoSelector.tsx | 7 +- src/pages/story/components/TimelineVideo.tsx | 3 +- src/pages/story/service.ts | 67 ++++----- src/pages/user/login/index.tsx | 5 +- src/requestErrorConfig.ts | 7 +- src/services/albums/api.ts | 21 ++- src/services/collections/api.ts | 9 +- src/services/comments/api.ts | 9 +- src/services/config/apiUrls.ts | 131 ++++++++++++++++++ src/services/file/api.ts | 22 +-- src/services/preferences/api.ts | 11 +- src/services/profile/api.ts | 13 +- src/services/reactions/api.ts | 9 +- src/services/statistics/api.ts | 11 +- src/services/sync/api.ts | 9 +- src/services/user/api.ts | 9 +- src/services/user/typing.d.ts | 10 +- src/typings.d.ts | 26 ++-- src/utils/offline/db.ts | 9 +- src/utils/offline/syncManager.ts | 12 +- 29 files changed, 392 insertions(+), 225 deletions(-) create mode 100644 src/services/config/apiUrls.ts diff --git a/config/config.ts b/config/config.ts index f16dd13..62a5ade 100644 --- a/config/config.ts +++ b/config/config.ts @@ -160,56 +160,56 @@ export default defineConfig({ * @name Webpack configuration * @description Custom webpack configuration for code splitting and optimization */ - chainWebpack: (config: any) => { - // Ignore Windows system files to prevent Watchpack errors - config.watchOptions({ - ignored: [ - '**/node_modules', - '**/.git', - '**/C:/DumpStack.log.tmp', - '**/C:/pagefile.sys', - '**/C:/swapfile.sys', - '**/C:/System Volume Information', - '**/C:/$*', // Ignore all system files starting with $ - ], - }); + // chainWebpack: (config: any) => { + // // Ignore Windows system files to prevent Watchpack errors + // config.watchOptions({ + // ignored: [ + // '**/node_modules', + // '**/.git', + // '**/C:/DumpStack.log.tmp', + // '**/C:/pagefile.sys', + // '**/C:/swapfile.sys', + // '**/C:/System Volume Information', + // '**/C:/$*', // Ignore all system files starting with $ + // ], + // }); - // Split large vendor libraries into separate chunks - config.optimization.splitChunks({ - chunks: 'all', - cacheGroups: { - // Ant Design and related libraries - antd: { - name: 'antd', - test: /[\\/]node_modules[\\/](@ant-design|antd|@antv|rc-)/, - priority: 20, - }, - // React and related libraries - react: { - name: 'react', - test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom)/, - priority: 20, - }, - // Other vendor libraries - vendors: { - name: 'vendors', - test: /[\\/]node_modules[\\/]/, - priority: 10, - }, - }, - }); + // // Split large vendor libraries into separate chunks + // config.optimization.splitChunks({ + // chunks: 'all', + // cacheGroups: { + // // Ant Design and related libraries + // antd: { + // name: 'antd', + // test: /[\\/]node_modules[\\/](@ant-design|antd|@antv|rc-)/, + // priority: 20, + // }, + // // React and related libraries + // react: { + // name: 'react', + // test: /[\\/]node_modules[\\/](react|react-dom|react-router|react-router-dom)/, + // priority: 20, + // }, + // // Other vendor libraries + // vendors: { + // name: 'vendors', + // test: /[\\/]node_modules[\\/]/, + // priority: 10, + // }, + // }, + // }); - // Optimize images - config.module - .rule('images') - .test(/\.(png|jpe?g|gif|webp|svg)$/) - .use('url-loader') - .loader('url-loader') - .options({ - limit: 8192, // Inline images smaller than 8KB - name: 'static/images/[name].[hash:8].[ext]', - }); + // // Optimize images + // config.module + // .rule('images') + // .test(/\.(png|jpe?g|gif|webp|svg)$/) + // .use('url-loader') + // .loader('url-loader') + // .options({ + // limit: 8192, // Inline images smaller than 8KB + // name: 'static/images/[name].[hash:8].[ext]', + // }); - return config; - }, + // return config; + // }, }); diff --git a/config/proxy.ts b/config/proxy.ts index 525d2cb..fc7b89d 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -15,13 +15,22 @@ export default { // 如果需要自定义本地开发服务器 请取消注释按需调整 dev: { // localhost:8000/api/** -> https://preview.pro.ant.design/api/** + // 注意:更具体的路径应该放在前面,避免被通用路径覆盖 '/api/story/': { // 要代理的地址 target: basePath, // 配置了这个可以从 http 代理到 https // 依赖 origin 的功能可能需要这个,比如 cookie changeOrigin: true, - pathRewrite: { '^/api/story': '/api/story' }, + pathRewrite: { '^/api/story': '/story' }, + }, + '/api/user/': { + // 要代理的地址 + target: basePath, + // 配置了这个可以从 http 代理到 https + // 依赖 origin 的功能可能需要这个,比如 cookie + changeOrigin: true, + pathRewrite: { '^/api/user': '/user' }, }, '/file/': { // 要代理的地址 @@ -39,10 +48,11 @@ export default { changeOrigin: true, pathRewrite: { '^/user-api': '/user' }, }, + // 通用 /api/ 代理放在最后,避免覆盖上面的具体配置 '/api/': { - target: 'https://proapi.azurewebsites.net', + target: basePath, changeOrigin: true, - pathRewrite: { '^': '' }, + pathRewrite: { '^/api': '/api' }, }, }, diff --git a/src/app.tsx b/src/app.tsx index 22416e9..183d9ae 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -8,7 +8,6 @@ import { history, Link, useModel } from '@umijs/max'; import { useIsMobile } from '@/hooks/useIsMobile'; import defaultSettings from '../config/defaultSettings'; import { errorConfig } from './requestErrorConfig'; -import { currentUser as queryCurrentUser } from './services/ant-design-pro/api'; import { useNotifications } from '@/hooks/useNotifications'; import BottomNav from '@/components/BottomNav'; import SyncStatusIndicator from '@/components/SyncStatus/SyncStatusIndicator'; @@ -22,32 +21,33 @@ const loginPath = '/user/login'; * Layout Children Wrapper Component * Handles mobile detection and notifications setup */ -function LayoutChildrenWrapper({ - children, - initialState, - setInitialState -}: { +function LayoutChildrenWrapper({ + children, + initialState, + setInitialState +}: { children: React.ReactNode; initialState: any; setInitialState: any; }) { const isMobile = useIsMobile(); - const { effectiveTheme, getCurrentColorScheme, fetchThemePreferences } = useModel('theme'); + // 暂时注释掉 theme 相关代码,避免 useModel 调用导致问题 + const { effectiveTheme, getCurrentColorScheme, fetchThemePreferences } = useModel('theme'); useEffect(() => { fetchThemePreferences(); }, [fetchThemePreferences]); - const colorScheme = getCurrentColorScheme(); + // 暂时注释掉 useNotifications,避免 useModel 调用导致问题 useNotifications(); return ( @@ -77,24 +77,26 @@ export async function getInitialState(): Promise<{ currentUser?: API.CurrentUser; loading?: boolean; }> { - const fetchUserInfo = async () => { - try { - const msg = await queryCurrentUser({ skipErrorHandler: true }); - return msg.data; - } catch (error) { - // history.push(loginPath); + // 从 localStorage 获取用户信息 + const getUserFromStorage = () => { + if (typeof window !== 'undefined') { + const userStr = localStorage.getItem('timeline_user'); + if (userStr) { + try { + return JSON.parse(userStr); + } catch (e) { + return undefined; + } + } } return undefined; }; - if (history.location.pathname !== loginPath) { - const currentUser = await fetchUserInfo(); - return { - currentUser, - settings: defaultSettings as Partial, - }; - } + // 直接从 localStorage 读取用户信息,避免API调用阻塞页面渲染 + const currentUser = getUserFromStorage(); + return { + currentUser, settings: defaultSettings as Partial, }; } @@ -103,8 +105,8 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) = return { actionsRender: () => [ - , - , + + , ], diff --git a/src/components/Comments/CommentList.tsx b/src/components/Comments/CommentList.tsx index a8f3ece..709955b 100644 --- a/src/components/Comments/CommentList.tsx +++ b/src/components/Comments/CommentList.tsx @@ -48,6 +48,9 @@ const CommentList: React.FC = ({ }) => { // Sort comments chronologically (oldest first) const sortedComments = useMemo(() => { + if (!comments || !Array.isArray(comments)) { + return []; + } return [...comments].sort((a, b) => { return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(); }); diff --git a/src/hooks/useNotifications.ts b/src/hooks/useNotifications.ts index 0ac7b9f..ee77cb6 100644 --- a/src/hooks/useNotifications.ts +++ b/src/hooks/useNotifications.ts @@ -3,6 +3,7 @@ import { useEffect, useState, useCallback } from 'react'; import { useModel, request } from '@umijs/max'; import { notification as antdNotification } from 'antd'; import { Notification, NotificationType } from '@/types'; +import { AUTH_API, USER_API } from '@/services/config/apiUrls'; export const useNotifications = () => { const { initialState } = useModel('@@initialState'); @@ -13,7 +14,7 @@ export const useNotifications = () => { const fetchUnreadNotifications = useCallback(async () => { if (!initialState?.currentUser) return; try { - const res = await request('/user-api/message/unread'); + const res = await request(`${AUTH_API.INFO}/message/unread`); setNotifications(res); setUnreadCount(res.length); } catch (error) { @@ -53,7 +54,7 @@ export const useNotifications = () => { const markAsRead = useCallback(async (ids: number[]) => { try { - await request('/api/user/notifications/read', { + await request(USER_API.NOTIFICATIONS_READ, { method: 'POST', data: ids, }); @@ -79,9 +80,14 @@ export const useNotifications = () => { case NotificationType.SYSTEM: return '系统通知'; default: - return '系统通知'; + return '通知'; } }; - return { notifications, unreadCount, markAsRead }; + return { + notifications, + unreadCount, + markAsRead, + fetchUnreadNotifications, + }; }; diff --git a/src/hooks/useReactions.ts b/src/hooks/useReactions.ts index a41fa3e..2281760 100644 --- a/src/hooks/useReactions.ts +++ b/src/hooks/useReactions.ts @@ -156,7 +156,7 @@ export function useReactions( // Helpers hasReaction: !!userReaction, - totalReactions: reactions + totalReactions: reactions?.counts ? Object.values(reactions.counts).reduce((sum, count) => sum + count, 0) : 0, }; diff --git a/src/models/sync.ts b/src/models/sync.ts index 583c4ae..c91b53a 100644 --- a/src/models/sync.ts +++ b/src/models/sync.ts @@ -15,7 +15,7 @@ export interface SyncState { export default () => { const [syncState, setSyncState] = useState({ - isOnline: navigator.onLine, + isOnline: typeof window !== 'undefined' ? navigator.onLine : true, isSyncing: false, pendingChanges: 0, lastSyncTime: undefined, @@ -24,6 +24,8 @@ export default () => { // Update online status useEffect(() => { + if (typeof window === 'undefined') return; + const handleOnline = () => { setSyncState((prev) => ({ ...prev, isOnline: true, syncError: undefined })); // Automatically trigger sync when coming online diff --git a/src/pages/account/center/service.ts b/src/pages/account/center/service.ts index 255f85a..3295f66 100644 --- a/src/pages/account/center/service.ts +++ b/src/pages/account/center/service.ts @@ -1,19 +1,20 @@ import { request } from '@umijs/max'; import type { CurrentUser, FriendUser, HistoryMessage, ListItemDataType, MessageItem, UserInfo } from './data.d'; import { CommonResponse } from '@/types/common'; +import { AUTH_API, STORY_API } from '@/services/config/apiUrls'; export async function queryCurrent(): Promise<{ data: CurrentUser }> { - return request('/user-api/info'); + return request(AUTH_API.INFO); } export async function searchUsername(params: {username: string}) : Promise<{data: UserInfo[]}> { - return request('/user-api/search', { + return request(AUTH_API.FRIEND_SEARCH, { params: params, method: "GET" }) } export async function addFriend(userId: string) : Promise> { - return request('/user-api/friend/request', { + return request(AUTH_API.FRIEND_REQUEST, { data: { friendId: userId, }, @@ -21,29 +22,29 @@ export async function addFriend(userId: string) : Promise }) } export async function queryFriendList(): Promise> { - return request('/user-api/friend/list'); + return request(AUTH_API.FRIEND_LIST); } export async function acceptFriendRequest(friendId: string): Promise> { - return request('/user-api/friend/accept', { + return request(AUTH_API.FRIEND_ACCEPT, { method: 'POST', data: { friendId }, }); } export async function rejectFriendRequest(friendId: string): Promise> { - return request('/user-api/friend/reject', { + return request(AUTH_API.FRIEND_REJECT, { method: 'POST', data: { friendId }, }); } export async function queryHistoryMessages(): Promise> { - return request('/user-api/message/history/friend', { + return request(AUTH_API.MESSAGE_HISTORY, { method: 'GET', }); } export async function queryFriendDynamic(): Promise> { - return request('/api/story/activity/authorized-items', { + return request(STORY_API.ACTIVITY_AUTHORIZED_ITEMS, { method: 'GET', }); } diff --git a/src/pages/account/settings/service.ts b/src/pages/account/settings/service.ts index f6eb2f0..a694093 100644 --- a/src/pages/account/settings/service.ts +++ b/src/pages/account/settings/service.ts @@ -1,24 +1,25 @@ import { request } from '@umijs/max'; import type { CurrentUser, GeographicItemType } from './data'; import { CommonResponse } from '@/types/common'; +import { AUTH_API, GEO_API, COMMON_API } from '@/services/config/apiUrls'; export async function queryCurrent(): Promise<{ data: CurrentUser }> { - return request('/user-api/info'); + return request(AUTH_API.INFO); } export async function updateCurrentUser(params: CurrentUser): Promise<{data: CommonResponse}> { - return request('/user-api/info', { + return request(AUTH_API.INFO, { method: 'PUT', data: params, }) } export async function queryProvince(): Promise<{ data: GeographicItemType[] }> { - return request('/api/geographic/province'); + return request(GEO_API.PROVINCE); } export async function queryCity(province: string): Promise<{ data: GeographicItemType[] }> { - return request(`/api/geographic/city/${province}`); + return request(GEO_API.CITY(province)); } export async function query() { - return request('/api/users'); + return request(COMMON_API.USERS); } diff --git a/src/pages/albums/components/PhotoSelector.tsx b/src/pages/albums/components/PhotoSelector.tsx index 8b12afc..27a9a23 100644 --- a/src/pages/albums/components/PhotoSelector.tsx +++ b/src/pages/albums/components/PhotoSelector.tsx @@ -7,6 +7,7 @@ import React, { useState, useEffect } from 'react'; import { Button, message, Spin, Empty, Checkbox } from 'antd'; import { useModel, request } from '@umijs/max'; +import { USER_API } from '@/services/config/apiUrls'; import styles from '../index.less'; interface PhotoSelectorProps { @@ -42,16 +43,16 @@ const PhotoSelector: React.FC = ({ setLoading(true); try { // Fetch user's photos from gallery - const response = await request('/api/v1/gallery/photos', { + const response = await request(USER_API.GALLERY_PHOTOS, { method: 'GET', params: { pageSize: 100 }, }); - + // Filter out photos already in the album const availablePhotos = response.items?.filter( (photo: Photo) => !existingPhotoIds.includes(photo.id) ) || []; - + setPhotos(availablePhotos); } catch (error) { message.error('Failed to load photos'); diff --git a/src/pages/story/components/TimelineVideo.tsx b/src/pages/story/components/TimelineVideo.tsx index 7ec3c9b..747d277 100644 --- a/src/pages/story/components/TimelineVideo.tsx +++ b/src/pages/story/components/TimelineVideo.tsx @@ -2,6 +2,7 @@ import React, { useState, useRef, useEffect } from 'react'; import { PlayCircleOutlined, PauseCircleOutlined, FullscreenOutlined, FullscreenExitOutlined } from '@ant-design/icons'; import { Spin } from 'antd'; import { request } from '@umijs/max'; +import { FILE_API } from '@/services/config/apiUrls'; interface TimelineVideoProps { videoInstanceId: string; @@ -21,7 +22,7 @@ const TimelineVideo: React.FC = ({ videoInstanceId, thumbnai useEffect(() => { const fetchVideoUrl = async () => { try { - const response = await request(`/file/get-video-url/${videoInstanceId}`, { method: 'GET' }); + const response = await request(FILE_API.VIDEO_URL(videoInstanceId), { method: 'GET' }); if (response.code === 200 && response.data) { setVideoSrc(response.data); } diff --git a/src/pages/story/service.ts b/src/pages/story/service.ts index aebdfc7..8c5c1e1 100644 --- a/src/pages/story/service.ts +++ b/src/pages/story/service.ts @@ -1,6 +1,7 @@ import { request } from '@umijs/max'; import {StoryItem, StoryItemTimeQueryParams, StoryType} from './data.d'; import {CommonListResponse, CommonResponse} from "@/types/common"; +import { STORY_API, FILE_API } from '@/services/config/apiUrls'; type ParamsType = { count?: number; @@ -13,19 +14,19 @@ type ParamsType = { export async function queryTimelineList( params: ParamsType, ): Promise<{ data: StoryType[] }> { - return await request('/api/story/list', { + return await request(STORY_API.LIST, { params, }); } export async function deleteStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> { - return request(`/api/story/${params.instanceId}`, { + return request(STORY_API.DETAIL(params.instanceId!), { method: 'DELETE', }); } export async function addStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> { - return request('/api/story/add', { + return request(STORY_API.ADD, { method: 'POST', data: { ...params, @@ -35,7 +36,7 @@ export async function addStory(params: ParamsType): Promise<{ data: { list: Stor } export async function updateStory(params: ParamsType): Promise<{ data: { list: StoryType[] } }> { - return await request(`/api/story/${params.instanceId}`, { + return await request(STORY_API.DETAIL(params.instanceId!), { method: 'PUT', data: { ...params, @@ -44,12 +45,12 @@ export async function updateStory(params: ParamsType): Promise<{ data: { list: S }); } export async function queryStoryDetail(itemId: string): Promise<{ data: StoryType }> { - return request(`/api/story/${itemId}`, { + return request(STORY_API.DETAIL(itemId), { method: 'GET', }); } export async function addStoryItem(params: FormData): Promise { - return request(`/api/story/item`, { + return request(STORY_API.ITEM, { method: 'POST', data: params, requestType: 'form', @@ -57,7 +58,7 @@ export async function addStoryItem(params: FormData): Promise { }); } export async function updateStoryItem(params: FormData): Promise { - return request(`/api/story/item`, { + return request(STORY_API.ITEM, { method: 'PUT', data: params, requestType: 'form', @@ -66,43 +67,43 @@ export async function updateStoryItem(params: FormData): Promise { } export async function queryStoryItem(params: ParamsType): Promise<{ data: CommonListResponse }> { - return request(`/api/story/item/list`, { + return request(STORY_API.ITEM_LIST, { method: 'GET', params: params, }); } export async function queryStoryItemDetail(itemId: string): Promise<{ data: StoryItem }> { - return request(`/api/story/item/${itemId}`, { + return request(STORY_API.ITEM_DETAIL(itemId), { method: 'GET', }); } export async function countStoryItem(storyInstanceId: string): Promise<{ data: StoryItem }> { - return request(`/api/story/item/count/${storyInstanceId}`, { + return request(STORY_API.ITEM_COUNT(storyInstanceId), { method: 'GET', }); } export async function queryStoryItemImages(itemId: string): Promise<{ data: string[] }> { - return request(`/api/story/item/images/${itemId}`, { + return request(STORY_API.ITEM_IMAGES(itemId), { method: 'GET', }); } export async function removeStoryItem(instanceId: string): Promise> { - return request(`/api/story/item/${instanceId}`, { + return request(STORY_API.ITEM_DETAIL(instanceId), { method: 'DELETE', }); } export async function searchStoryItems(params: { keyword: string; pageNum: number; pageSize: number }) { - return request('/api/story/item/search', { + return request(STORY_API.ITEM_SEARCH, { method: 'GET', params, }); } export async function fetchImage(imageInstanceId: string): Promise { - return request(`/file/image/${imageInstanceId}`, { + return request(FILE_API.IMAGE(imageInstanceId), { method: 'GET', responseType: 'blob', getResponse: true, @@ -110,59 +111,59 @@ export async function fetchImage(imageInstanceId: string): Promise { } export async function authorizeStoryPermission(params: {userId: string, storyInstanceId: string, permissionType: number}) { - return request('/api/story/permission/authorize', { + return request(STORY_API.PERMISSION_AUTHORIZE, { method: 'POST', data: params, }); } export async function getStoryPermissions(storyId: string) { - return request(`/api/story/permission/story/${storyId}`, { + return request(STORY_API.PERMISSION_STORY(storyId), { method: 'GET', }); } export async function inviteUser(params: {userId: string, storyInstanceId: string, permissionType: number}) { - return request('/api/story/permission/invite', { + return request(STORY_API.PERMISSION_INVITE, { method: 'POST', data: params, }); } export async function acceptInvite(inviteId: string) { - return request(`/api/story/permission/invite/${inviteId}/accept`, { + return request(STORY_API.PERMISSION_INVITE_ACCEPT(inviteId), { method: 'PUT', }); } export async function rejectInvite(inviteId: string) { - return request(`/api/story/permission/invite/${inviteId}/reject`, { + return request(STORY_API.PERMISSION_INVITE_REJECT(inviteId), { method: 'PUT', }); } export async function updatePermission(params: {permissionId: string, permissionType: number}) { - return request('/api/story/permission', { + return request(STORY_API.PERMISSION, { method: 'PUT', data: params }); } export async function removePermission(permissionId: string) { - return request(`/api/story/permission/${permissionId}`, { + return request(`${STORY_API.PERMISSION}/${permissionId}`, { method: 'DELETE' }); } /** * 更新时间线节点排序 - * + * * 功能描述: * 批量更新节点的排序值,用于拖拽排序后保存结果。 - * + * * @param orderData - 排序数据数组,包含节点ID和新排序值 * @returns API响应 - * + * * @example * const orderData = [ * { instanceId: 'item-1', sortOrder: 0 }, @@ -173,7 +174,7 @@ export async function removePermission(permissionId: string) { export async function updateStoryItemOrder( orderData: Array<{ instanceId: string; sortOrder: number }> ): Promise> { - return request('/api/story/item/order', { + return request(`${STORY_API.ITEM}/order`, { method: 'PUT', data: { items: orderData }, }); @@ -181,21 +182,21 @@ export async function updateStoryItemOrder( /** * 批量删除时间线节点 - * + * * 功能描述: * 根据节点ID列表批量删除时间线节点。 * 删除操作为软删除,数据可恢复。 - * + * * @param instanceIds - 要删除的节点ID数组 * @returns API响应 - * + * * @example * await batchDeleteStoryItems(['item-1', 'item-2', 'item-3']); */ export async function batchDeleteStoryItems( instanceIds: string[] ): Promise> { - return request('/api/story/item/batch-delete', { + return request(`${STORY_API.ITEM}/batch-delete`, { method: 'POST', data: { instanceIds }, }); @@ -203,10 +204,10 @@ export async function batchDeleteStoryItems( /** * 批量修改时间线节点时间 - * + * * 功能描述: * 批量修改多个节点的时间信息。 - * + * * @param instanceIds - 要修改的节点ID数组 * @param storyItemTime - 新的时间值 * @returns API响应 @@ -215,8 +216,8 @@ export async function batchUpdateStoryItemTime( instanceIds: string[], storyItemTime: string ): Promise> { - return request('/api/story/item/batch-time', { + return request(`${STORY_API.ITEM}/batch-time`, { method: 'PUT', data: { instanceIds, storyItemTime }, }); -} \ No newline at end of file +} diff --git a/src/pages/user/login/index.tsx b/src/pages/user/login/index.tsx index e5093af..c68f185 100644 --- a/src/pages/user/login/index.tsx +++ b/src/pages/user/login/index.tsx @@ -1,5 +1,6 @@ import { getFakeCaptcha } from '@/services/ant-design-pro/login'; import { loginUser } from '@/services/user/api'; +import type { UserLoginParams, UserLoginResult } from '@/services/user/typing'; import { CommonResponse } from '@/types/common'; import { AlipayCircleOutlined, @@ -133,9 +134,9 @@ const Login: React.FC = () => { currentUser: response.data, })); const urlParams = new URL(window.location.href).searchParams; - // 修复:直接使用 redirect 参数,如果不存在则跳转到首页 + // 修复:使用 history.push 进行跳转,避免页面刷新导致状态丢失 const redirect = urlParams.get('redirect'); - window.location.href = redirect || '/'; + history.push(redirect || '/'); return; } console.log(response.message); diff --git a/src/requestErrorConfig.ts b/src/requestErrorConfig.ts index 524ec20..838534b 100644 --- a/src/requestErrorConfig.ts +++ b/src/requestErrorConfig.ts @@ -120,8 +120,13 @@ export const errorConfig: RequestConfig = { return { ...config, headers }; } - // 无 token 提示并引导登录 + // 无 token 时,如果是获取当前用户信息的请求,不提示登录(让调用方处理) if (!token) { + const skipAuthUrls = ['/api/currentUser', '/user-api/info']; + if (config?.url && skipAuthUrls.some((u) => config.url?.includes(u))) { + return config; + } + message.warning('请先登录'); const currentUrl = window.location.href; const urlParams = new URL(currentUrl).searchParams; diff --git a/src/services/albums/api.ts b/src/services/albums/api.ts index 7e263df..a38a71a 100644 --- a/src/services/albums/api.ts +++ b/src/services/albums/api.ts @@ -4,14 +4,13 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/albums'; +import { USER_API } from '@/services/config/apiUrls'; /** * Get all albums for the current user */ export async function getAlbums(): Promise { - return request(`${BASE_URL}`, { + return request(USER_API.ALBUMS, { method: 'GET', }); } @@ -20,7 +19,7 @@ export async function getAlbums(): Promise { * Get album details by ID */ export async function getAlbumById(id: string): Promise { - return request(`${BASE_URL}/${id}`, { + return request(USER_API.ALBUM_DETAIL(id), { method: 'GET', }); } @@ -29,7 +28,7 @@ export async function getAlbumById(id: string): Promise { * Create a new album */ export async function createAlbum(data: API.CreateAlbumDTO): Promise { - return request(`${BASE_URL}`, { + return request(USER_API.ALBUMS, { method: 'POST', data, }); @@ -39,7 +38,7 @@ export async function createAlbum(data: API.CreateAlbumDTO): Promise * Update an existing album */ export async function updateAlbum(id: string, data: API.UpdateAlbumDTO): Promise { - return request(`${BASE_URL}/${id}`, { + return request(USER_API.ALBUM_DETAIL(id), { method: 'PUT', data, }); @@ -49,7 +48,7 @@ export async function updateAlbum(id: string, data: API.UpdateAlbumDTO): Promise * Delete an album */ export async function deleteAlbum(id: string): Promise { - return request(`${BASE_URL}/${id}`, { + return request(USER_API.ALBUM_DETAIL(id), { method: 'DELETE', }); } @@ -58,7 +57,7 @@ export async function deleteAlbum(id: string): Promise { * Add photos to an album */ export async function addPhotosToAlbum(id: string, photoIds: string[]): Promise { - return request(`${BASE_URL}/${id}/photos`, { + return request(USER_API.ALBUM_PHOTOS(id), { method: 'POST', data: { photoIds }, }); @@ -68,7 +67,7 @@ export async function addPhotosToAlbum(id: string, photoIds: string[]): Promise< * Remove photos from an album */ export async function removePhotosFromAlbum(id: string, photoIds: string[]): Promise { - return request(`${BASE_URL}/${id}/photos`, { + return request(USER_API.ALBUM_PHOTOS(id), { method: 'DELETE', data: { photoIds }, }); @@ -78,7 +77,7 @@ export async function removePhotosFromAlbum(id: string, photoIds: string[]): Pro * Reorder photos in an album */ export async function reorderPhotos(id: string, order: string[]): Promise { - return request(`${BASE_URL}/${id}/photos/order`, { + return request(USER_API.ALBUM_PHOTOS_ORDER(id), { method: 'PUT', data: { order }, }); @@ -88,7 +87,7 @@ export async function reorderPhotos(id: string, order: string[]): Promise * Set album cover photo */ export async function setAlbumCover(id: string, coverPhotoId: string): Promise { - return request(`${BASE_URL}/${id}/cover`, { + return request(USER_API.ALBUM_COVER(id), { method: 'PUT', data: { coverPhotoId }, }); diff --git a/src/services/collections/api.ts b/src/services/collections/api.ts index c108dbb..7c17c85 100644 --- a/src/services/collections/api.ts +++ b/src/services/collections/api.ts @@ -4,14 +4,13 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/collections'; +import { USER_API } from '@/services/config/apiUrls'; /** * Get all smart collections for the current user */ export async function getSmartCollections(): Promise { - return request(`${BASE_URL}/smart`, { + return request(USER_API.COLLECTIONS_SMART, { method: 'GET', }); } @@ -23,7 +22,7 @@ export async function getCollectionContent( collectionId: string, params: { page: number; pageSize: number }, ): Promise { - return request(`${BASE_URL}/smart/${collectionId}/content`, { + return request(USER_API.COLLECTIONS_SMART_CONTENT(collectionId), { method: 'GET', params, }); @@ -33,7 +32,7 @@ export async function getCollectionContent( * Force refresh smart collections */ export async function refreshSmartCollections(): Promise { - return request(`${BASE_URL}/smart/refresh`, { + return request(USER_API.COLLECTIONS_SMART_REFRESH, { method: 'POST', }); } diff --git a/src/services/comments/api.ts b/src/services/comments/api.ts index 9d55263..b7c7f76 100644 --- a/src/services/comments/api.ts +++ b/src/services/comments/api.ts @@ -4,6 +4,7 @@ */ import { request } from '@umijs/max'; +import { SOCIAL_API } from '@/services/config/apiUrls'; /** * Get comments for an entity @@ -12,7 +13,7 @@ export async function getComments( entityType: API.SocialEntityType, entityId: string, ): Promise { - return request(`/api/v1/comments/${entityType}/${entityId}`, { + return request(SOCIAL_API.COMMENTS_BY_ENTITY(entityType, entityId), { method: 'GET', }); } @@ -21,7 +22,7 @@ export async function getComments( * Create a new comment */ export async function createComment(data: API.CreateCommentDTO): Promise { - return request('/api/v1/comments', { + return request(SOCIAL_API.COMMENTS, { method: 'POST', data, }); @@ -34,7 +35,7 @@ export async function updateComment( id: string, data: API.UpdateCommentDTO, ): Promise { - return request(`/api/v1/comments/${id}`, { + return request(SOCIAL_API.COMMENTS_DETAIL(id), { method: 'PUT', data, }); @@ -44,7 +45,7 @@ export async function updateComment( * Delete a comment */ export async function deleteComment(id: string): Promise { - return request(`/api/v1/comments/${id}`, { + return request(SOCIAL_API.COMMENTS_DETAIL(id), { method: 'DELETE', }); } diff --git a/src/services/config/apiUrls.ts b/src/services/config/apiUrls.ts new file mode 100644 index 0000000..44ac27b --- /dev/null +++ b/src/services/config/apiUrls.ts @@ -0,0 +1,131 @@ +/** + * API URL 全局配置 + * 所有后端接口地址统一在此配置,便于管理和维护 + */ + +// 基础 API 前缀 +const API_PREFIX = '/api'; +const USER_API_PREFIX = `${API_PREFIX}/user/api/v1`; +const USER_AUTH_PREFIX = '/user-api'; + +// Story 相关接口 +export const STORY_API = { + BASE: `${API_PREFIX}/story`, + LIST: `${API_PREFIX}/story/list`, + ADD: `${API_PREFIX}/story/add`, + DETAIL: (id: string) => `${API_PREFIX}/story/${id}`, + + // Story Item 相关 + ITEM: `${API_PREFIX}/story/item`, + ITEM_LIST: `${API_PREFIX}/story/item/list`, + ITEM_DETAIL: (id: string) => `${API_PREFIX}/story/item/${id}`, + ITEM_COUNT: (storyInstanceId: string) => `${API_PREFIX}/story/item/count/${storyInstanceId}`, + ITEM_IMAGES: (itemId: string) => `${API_PREFIX}/story/item/images/${itemId}`, + ITEM_SEARCH: `${API_PREFIX}/story/item/search`, + + // 权限相关 + PERMISSION: `${API_PREFIX}/story/permission`, + PERMISSION_AUTHORIZE: `${API_PREFIX}/story/permission/authorize`, + PERMISSION_STORY: (storyId: string) => `${API_PREFIX}/story/permission/story/${storyId}`, + PERMISSION_INVITE: `${API_PREFIX}/story/permission/invite`, + PERMISSION_INVITE_ACCEPT: (inviteId: string) => `${API_PREFIX}/story/permission/invite/${inviteId}/accept`, + PERMISSION_INVITE_REJECT: (inviteId: string) => `${API_PREFIX}/story/permission/invite/${inviteId}/reject`, + + // 活动相关 + ACTIVITY_AUTHORIZED_ITEMS: `${API_PREFIX}/story/activity/authorized-items`, +} as const; + +// 文件相关接口 +export const FILE_API = { + IMAGE: (imageInstanceId: string) => `${API_PREFIX}/file/image/${imageInstanceId}`, + IMAGE_LOW_RES: (imageInstanceId: string) => `${API_PREFIX}/file/image-low-res/${imageInstanceId}`, + IMAGE_LIST: `${API_PREFIX}/file/image/list`, + VIDEO_URL: (videoInstanceId: string) => `${API_PREFIX}/file/get-video-url/${videoInstanceId}`, + UPLOAD_IMAGE: `${API_PREFIX}/file/upload-image`, + GET_UPLOAD_URL: (fileName: string) => `${API_PREFIX}/file/get-upload-url/${fileName}`, + UPLOADED: `${API_PREFIX}/file/uploaded`, + BATCH_INFO: `${API_PREFIX}/file/batch-info`, +} as const; + +// 用户相关接口 +export const USER_API = { + PROFILE: `${USER_API_PREFIX}/profile`, + PROFILE_COVER: `${USER_API_PREFIX}/profile/cover`, + PROFILE_CUSTOM_FIELDS: `${USER_API_PREFIX}/profile/custom-fields`, + + PREFERENCES: `${USER_API_PREFIX}/preferences`, + PREFERENCES_THEME: `${USER_API_PREFIX}/preferences/theme`, + PREFERENCES_LAYOUT: `${USER_API_PREFIX}/preferences/layout`, + PREFERENCES_TIMELINE: `${USER_API_PREFIX}/preferences/timeline`, + + ALBUMS: `${USER_API_PREFIX}/albums`, + ALBUM_DETAIL: (id: string) => `${USER_API_PREFIX}/albums/${id}`, + ALBUM_PHOTOS: (id: string) => `${USER_API_PREFIX}/albums/${id}/photos`, + ALBUM_PHOTOS_ORDER: (id: string) => `${USER_API_PREFIX}/albums/${id}/photos/order`, + ALBUM_COVER: (id: string) => `${USER_API_PREFIX}/albums/${id}/cover`, + + COLLECTIONS: `${USER_API_PREFIX}/collections`, + COLLECTIONS_SMART: `${USER_API_PREFIX}/collections/smart`, + COLLECTIONS_SMART_CONTENT: (collectionId: string) => `${USER_API_PREFIX}/collections/smart/${collectionId}/content`, + COLLECTIONS_SMART_REFRESH: `${USER_API_PREFIX}/collections/smart/refresh`, + + // Gallery 相关 + GALLERY_PHOTOS: `${API_PREFIX}/v1/gallery/photos`, + + // 通知相关 + NOTIFICATIONS_READ: `${API_PREFIX}/user/notifications/read`, +} as const; + +// 用户认证相关接口 +export const AUTH_API = { + INFO: `${USER_AUTH_PREFIX}/info`, + REGISTER: `${USER_AUTH_PREFIX}/auth/register`, + LOGIN: `${USER_AUTH_PREFIX}/auth/login`, + LOGOUT: `${USER_AUTH_PREFIX}/auth/logout`, + + // 好友相关 + FRIEND_SEARCH: `${USER_AUTH_PREFIX}/search`, + FRIEND_REQUEST: `${USER_AUTH_PREFIX}/friend/request`, + FRIEND_LIST: `${USER_AUTH_PREFIX}/friend/list`, + FRIEND_ACCEPT: `${USER_AUTH_PREFIX}/friend/accept`, + FRIEND_REJECT: `${USER_AUTH_PREFIX}/friend/reject`, + MESSAGE_HISTORY: `${USER_AUTH_PREFIX}/message/history/friend`, +} as const; + +// 社交功能接口 +export const SOCIAL_API = { + COMMENTS: `${API_PREFIX}/v1/comments`, + COMMENTS_DETAIL: (id: string) => `${API_PREFIX}/v1/comments/${id}`, + COMMENTS_BY_ENTITY: (entityType: string, entityId: string) => `${API_PREFIX}/v1/comments/${entityType}/${entityId}`, + + REACTIONS: `${API_PREFIX}/v1/reactions`, + REACTIONS_BY_ENTITY: (entityType: string, entityId: string) => `${API_PREFIX}/v1/reactions/${entityType}/${entityId}`, +} as const; + +// 同步相关接口 +export const SYNC_API = { + BASE: `${API_PREFIX}/v1/sync`, + CHANGES: `${API_PREFIX}/v1/sync/changes`, + STATUS: `${API_PREFIX}/v1/sync/status`, + RESOLVE_CONFLICT: `${API_PREFIX}/v1/sync/resolve-conflict`, +} as const; + +// 统计相关接口 +export const STATISTICS_API = { + BASE: `${API_PREFIX}/v1/statistics`, + OVERVIEW: `${API_PREFIX}/v1/statistics/overview`, + UPLOADS: `${API_PREFIX}/v1/statistics/uploads`, + STORAGE: `${API_PREFIX}/v1/statistics/storage`, + REFRESH: `${API_PREFIX}/v1/statistics/refresh`, +} as const; + +// 地理信息接口 +export const GEO_API = { + PROVINCE: `${API_PREFIX}/geographic/province`, + CITY: (province: string) => `${API_PREFIX}/geographic/city/${province}`, +} as const; + +// 其他通用接口 +export const COMMON_API = { + USERS: `${API_PREFIX}/users`, +} as const; diff --git a/src/services/file/api.ts b/src/services/file/api.ts index 2f17986..c490534 100644 --- a/src/services/file/api.ts +++ b/src/services/file/api.ts @@ -1,22 +1,24 @@ import {request} from "@@/exports"; import {CommonListResponse, CommonResponse} from "@/types/common"; import {ImageItem} from "@/pages/gallery/typings"; +import { STORY_API, FILE_API } from '@/services/config/apiUrls'; + // 查询storyItem图片列表 export async function queryStoryItemImages(itemId: string): Promise<{ data: string[] }> { - return request(`/api/story/item/images/${itemId}`, { + return request(STORY_API.ITEM_IMAGES(itemId), { method: 'GET', }); } // 获取图片 export async function fetchImage(imageInstanceId: string): Promise { - return request(`/file/image/${imageInstanceId}`, { + return request(FILE_API.IMAGE(imageInstanceId), { method: 'GET', responseType: 'blob', getResponse: true, }); } export async function fetchImageLowRes(imageInstanceId: string): Promise { - return request(`/file/image-low-res/${imageInstanceId}`, { + return request(FILE_API.IMAGE_LOW_RES(imageInstanceId), { method: 'GET', responseType: 'blob', getResponse: true, @@ -32,7 +34,7 @@ export async function getImagesList( }, options?: { [key: string]: any }, ) { - return request>>('/file/image/list', { + return request>>(FILE_API.IMAGE_LIST, { method: 'GET', params: { ...params, @@ -42,7 +44,7 @@ export async function getImagesList( } export async function deleteImage(params: { instanceId: string }) { - return request>(`/file/image/${params.instanceId}`, { + return request>(FILE_API.IMAGE(params.instanceId), { method: 'DELETE', params: { ...params, @@ -50,33 +52,33 @@ export async function deleteImage(params: { instanceId: string }) { }); } export async function uploadImage(params: FormData) { - return request>('/file/upload-image', { + return request>(FILE_API.UPLOAD_IMAGE, { method: 'POST', data: params, }); } export async function getUploadUrl(fileName: string) { - return request>(`/file/get-upload-url/${fileName}`, { + return request>(FILE_API.GET_UPLOAD_URL(fileName), { method: 'GET', }); } export async function getVideoUrl(instanceId: string): Promise> { - return request>(`/file/get-video-url/${instanceId}`, { + return request>(FILE_API.VIDEO_URL(instanceId), { method: 'GET', }); } export async function saveFileMetadata(data: any) { - return request>('/file/uploaded', { + return request>(FILE_API.UPLOADED, { method: 'POST', data, }); } export async function batchGetFileInfo(instanceIds: string[]): Promise> { - return request>('/file/batch-info', { + return request>(FILE_API.BATCH_INFO, { method: 'POST', data: instanceIds, }); diff --git a/src/services/preferences/api.ts b/src/services/preferences/api.ts index 72d2287..9381231 100644 --- a/src/services/preferences/api.ts +++ b/src/services/preferences/api.ts @@ -4,14 +4,13 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/preferences'; +import { USER_API } from '@/services/config/apiUrls'; /** * Get user preferences */ export async function getPreferences(): Promise { - return request(`${BASE_URL}`, { + return request(USER_API.PREFERENCES, { method: 'GET', }); } @@ -20,7 +19,7 @@ export async function getPreferences(): Promise { * Update theme preferences */ export async function updateThemePreferences(data: API.UpdateThemeDTO): Promise { - return request(`${BASE_URL}/theme`, { + return request(USER_API.PREFERENCES_THEME, { method: 'PUT', data, }); @@ -30,7 +29,7 @@ export async function updateThemePreferences(data: API.UpdateThemeDTO): Promise< * Update layout preferences */ export async function updateLayoutPreferences(data: API.UpdateLayoutDTO): Promise { - return request(`${BASE_URL}/layout`, { + return request(USER_API.PREFERENCES_LAYOUT, { method: 'PUT', data, }); @@ -40,7 +39,7 @@ export async function updateLayoutPreferences(data: API.UpdateLayoutDTO): Promis * Update timeline preferences */ export async function updateTimelinePreferences(data: API.UpdateTimelineDTO): Promise { - return request(`${BASE_URL}/timeline`, { + return request(USER_API.PREFERENCES_TIMELINE, { method: 'PUT', data, }); diff --git a/src/services/profile/api.ts b/src/services/profile/api.ts index 81a94dd..e1992c5 100644 --- a/src/services/profile/api.ts +++ b/src/services/profile/api.ts @@ -4,8 +4,7 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/profile'; +import { USER_API } from '@/services/config/apiUrls'; export interface UserProfile { userId: string; @@ -32,7 +31,7 @@ export interface ProfileData { * Get user profile */ export async function getProfile(): Promise { - return request(`${BASE_URL}`, { + return request(USER_API.PROFILE, { method: 'GET', }); } @@ -41,7 +40,7 @@ export async function getProfile(): Promise { * Update profile bio */ export async function updateProfile(data: { bio?: string }): Promise { - return request(`${BASE_URL}`, { + return request(USER_API.PROFILE, { method: 'PUT', data, }); @@ -53,8 +52,8 @@ export async function updateProfile(data: { bio?: string }): Promise { export async function uploadCoverPhoto(file: File): Promise { const formData = new FormData(); formData.append('file', file); - - return request(`${BASE_URL}/cover`, { + + return request(USER_API.PROFILE_COVER, { method: 'POST', data: formData, }); @@ -64,7 +63,7 @@ export async function uploadCoverPhoto(file: File): Promise { * Update custom fields */ export async function updateCustomFields(fields: CustomField[]): Promise { - return request(`${BASE_URL}/custom-fields`, { + return request(USER_API.PROFILE_CUSTOM_FIELDS, { method: 'PUT', data: { fields }, }); diff --git a/src/services/reactions/api.ts b/src/services/reactions/api.ts index 15931e6..9a81af8 100644 --- a/src/services/reactions/api.ts +++ b/src/services/reactions/api.ts @@ -4,8 +4,7 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/reactions'; +import { SOCIAL_API } from '@/services/config/apiUrls'; /** * Get reactions for an entity @@ -14,7 +13,7 @@ export async function getReactions( entityType: API.SocialEntityType, entityId: string, ): Promise { - return request(`${BASE_URL}/${entityType}/${entityId}`, { + return request(SOCIAL_API.REACTIONS_BY_ENTITY(entityType, entityId), { method: 'GET', }); } @@ -23,7 +22,7 @@ export async function getReactions( * Add or update a reaction */ export async function addReaction(data: API.AddReactionDTO): Promise { - return request(`${BASE_URL}`, { + return request(SOCIAL_API.REACTIONS, { method: 'POST', data, }); @@ -36,7 +35,7 @@ export async function removeReaction( entityType: API.SocialEntityType, entityId: string, ): Promise { - return request(`${BASE_URL}/${entityType}/${entityId}`, { + return request(SOCIAL_API.REACTIONS_BY_ENTITY(entityType, entityId), { method: 'DELETE', }); } diff --git a/src/services/statistics/api.ts b/src/services/statistics/api.ts index 8e38580..6188f0d 100644 --- a/src/services/statistics/api.ts +++ b/src/services/statistics/api.ts @@ -4,14 +4,13 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/statistics'; +import { STATISTICS_API } from '@/services/config/apiUrls'; /** * Get user statistics overview */ export async function getStatisticsOverview(): Promise { - return request(`${BASE_URL}/overview`, { + return request(STATISTICS_API.OVERVIEW, { method: 'GET', }); } @@ -20,7 +19,7 @@ export async function getStatisticsOverview(): Promise { * Get upload trends */ export async function getUploadTrends(): Promise { - return request(`${BASE_URL}/uploads`, { + return request(STATISTICS_API.UPLOADS, { method: 'GET', }); } @@ -29,7 +28,7 @@ export async function getUploadTrends(): Promise { * Get storage breakdown */ export async function getStorageBreakdown(): Promise { - return request(`${BASE_URL}/storage`, { + return request(STATISTICS_API.STORAGE, { method: 'GET', }); } @@ -38,7 +37,7 @@ export async function getStorageBreakdown(): Promise { * Force refresh statistics */ export async function refreshStatistics(): Promise { - return request(`${BASE_URL}/refresh`, { + return request(STATISTICS_API.REFRESH, { method: 'POST', }); } diff --git a/src/services/sync/api.ts b/src/services/sync/api.ts index c3dcc8d..b26303d 100644 --- a/src/services/sync/api.ts +++ b/src/services/sync/api.ts @@ -4,14 +4,13 @@ */ import { request } from '@umijs/max'; - -const BASE_URL = '/api/v1/sync'; +import { SYNC_API } from '@/services/config/apiUrls'; /** * Upload offline changes batch */ export async function uploadChanges(changes: API.ChangeRecord[]): Promise { - return request(`${BASE_URL}/changes`, { + return request(SYNC_API.CHANGES, { method: 'POST', data: { changes }, }); @@ -21,7 +20,7 @@ export async function uploadChanges(changes: API.ChangeRecord[]): Promise { - return request(`${BASE_URL}/status`, { + return request(SYNC_API.STATUS, { method: 'GET', }); } @@ -30,7 +29,7 @@ export async function getSyncStatus(): Promise<{ status: API.SyncStatus; pending * Resolve sync conflict */ export async function resolveConflict(resolution: API.Resolution): Promise { - return request(`${BASE_URL}/resolve-conflict`, { + return request(SYNC_API.RESOLVE_CONFLICT, { method: 'POST', data: resolution, }); diff --git a/src/services/user/api.ts b/src/services/user/api.ts index f3223f3..a0d17dd 100644 --- a/src/services/user/api.ts +++ b/src/services/user/api.ts @@ -1,23 +1,24 @@ import { CommonResponse } from "@/types/common"; import {request} from "@@/exports"; +import { AUTH_API } from '@/services/config/apiUrls'; export async function registerUser(params: UserRegisterParams, options?: { skipErrorHandler?: boolean }): Promise> { - return request('/user-api/auth/register', { + return request(AUTH_API.REGISTER, { method: 'POST', data: params, getResponse: true, }); } export async function loginUser(params: UserLoginParams): Promise> { - return request('/user-api/auth/login', { + return request(AUTH_API.LOGIN, { method: 'POST', data: params, // getResponse: true, }); } export async function logoutUser() { - return request('/user-api/auth/logout', { + return request(AUTH_API.LOGOUT, { method: 'POST', }); -} \ No newline at end of file +} diff --git a/src/services/user/typing.d.ts b/src/services/user/typing.d.ts index 6d23017..83cad09 100644 --- a/src/services/user/typing.d.ts +++ b/src/services/user/typing.d.ts @@ -1,5 +1,5 @@ // 注册用户参数 -interface UserRegisterParams { +export interface UserRegisterParams { username: string; nickname: string; password: string; @@ -7,25 +7,25 @@ interface UserRegisterParams { phone: string; } // 登录用户参数 -interface UserLoginParams { +export interface UserLoginParams { username?: string; password?: string; loginType?: string; } // 退出登录参数 -interface UserLogoutParams { +export interface UserLogoutParams { username: string; password: string; } // 登录返回参数 -interface UserLoginResult { +export interface UserLoginResult { accessToken: string; refreshToken: string; accessTokenExpiresInSeconds: number; refreshTokenExpiresInSeconds: number; } // 用户信息 -interface UserInfo { +export interface UserInfo { username: string, accessToken: string; refreshToken: string; diff --git a/src/typings.d.ts b/src/typings.d.ts index c4950a3..9097656 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -19,17 +19,17 @@ declare module 'react-masonry-css'; declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false; // Extend Namespaces type to include 'theme' -declare module '@umijs/max' { - export type Namespaces = - | 'menu' - | 'settings' - | 'pages' - | 'component' - | 'globalHeader' - | 'theme' - | 'sync' - | 'albums' - | 'collections' - | 'layout'; -} +// declare module '@umijs/max' { +// export type Namespaces = +// | 'menu' +// | 'settings' +// | 'pages' +// | 'component' +// | 'globalHeader' +// | 'theme' +// | 'sync' +// | 'albums' +// | 'collections' +// | 'layout'; +// } diff --git a/src/utils/offline/db.ts b/src/utils/offline/db.ts index 0763229..7059560 100644 --- a/src/utils/offline/db.ts +++ b/src/utils/offline/db.ts @@ -20,8 +20,8 @@ export class OfflineDatabase extends Dexie { super('TimelineOffline'); // Define database schema - // Version 1: Initial schema - this.version(1).stores({ + // Version 2: Added synced index to changes table + this.version(2).stores({ // Stories table with indexes // Primary key: id // Indexes: userId, createdAt, syncStatus for efficient querying @@ -34,9 +34,8 @@ export class OfflineDatabase extends Dexie { // Changes table with auto-incrementing primary key // Primary key: ++id (auto-increment) - // Indexes: entityType, entityId, operation, timestamp - // Note: synced is stored as 0/1 for IndexedDB compatibility but not indexed - changes: '++id, entityType, entityId, operation, timestamp', + // Indexes: entityType, entityId, operation, timestamp, synced + changes: '++id, entityType, entityId, operation, timestamp, synced', }); // Map tables to TypeScript types diff --git a/src/utils/offline/syncManager.ts b/src/utils/offline/syncManager.ts index 5648774..69178ed 100644 --- a/src/utils/offline/syncManager.ts +++ b/src/utils/offline/syncManager.ts @@ -18,19 +18,25 @@ import { message } from 'antd'; export class SyncManager { private queue: API.ChangeRecord[] = []; private syncing: boolean = false; - private online: boolean = navigator.onLine; + private online: boolean = typeof window !== 'undefined' ? navigator.onLine : true; private syncListeners: Array<(status: API.SyncStatus) => void> = []; private autoSyncEnabled: boolean = true; + private initialized: boolean = false; constructor() { - this.initializeEventListeners(); - this.loadQueueFromDB(); + // Defer initialization to avoid SSR issues + if (typeof window !== 'undefined') { + this.initializeEventListeners(); + this.loadQueueFromDB(); + this.initialized = true; + } } /** * Initialize online/offline event listeners */ private initializeEventListeners(): void { + if (typeof window === 'undefined') return; window.addEventListener('online', () => this.onOnline()); window.addEventListener('offline', () => this.onOffline()); }