feat: 优化架构
Some checks failed
Frontend CI / build-and-push-web (push) Successful in 2m39s
Frontend CI / ota-android (push) Successful in 10m22s
Frontend CI / build-android-apk (push) Has been cancelled

This commit is contained in:
lafay
2026-04-13 01:30:37 +08:00
parent 2adc9360a5
commit fe6a03da5d
93 changed files with 386 additions and 211 deletions

View File

@@ -3,8 +3,9 @@
*
*/
import { api } from './api';
import { User } from '../types';
import { api } from '../core/api';
import { User } from '@/types';
import type { PrivacySettingsDTO, PrivacySettingsRequestDTO, DeletionStatusDTO } from '@/types/dto';
export function resolveAuthApiError(error: any, fallback = '操作失败,请稍后重试'): string {
const code: number = error?.code ?? 0;
@@ -160,6 +161,7 @@ class AuthService {
if (refreshToken) {
await api.setRefreshToken(refreshToken);
}
api.resetAuthState();
return response.data;
}
@@ -178,6 +180,7 @@ class AuthService {
if (refreshToken) {
await api.setRefreshToken(refreshToken);
}
api.resetAuthState();
return response.data;
}
@@ -457,9 +460,9 @@ class AuthService {
}
// 隐私设置
async getPrivacySettings(): Promise<import('../types/dto').PrivacySettingsDTO | null> {
async getPrivacySettings(): Promise<PrivacySettingsDTO | null> {
try {
const response = await api.get<import('../types/dto').PrivacySettingsDTO>('/users/me/privacy');
const response = await api.get<PrivacySettingsDTO>('/users/me/privacy');
return response.data;
} catch (error) {
console.error('获取隐私设置失败:', error);
@@ -467,7 +470,7 @@ class AuthService {
}
}
async updatePrivacySettings(settings: import('../types/dto').PrivacySettingsRequestDTO): Promise<boolean> {
async updatePrivacySettings(settings: PrivacySettingsRequestDTO): Promise<boolean> {
try {
await api.put('/users/me/privacy', settings);
return true;
@@ -478,9 +481,9 @@ class AuthService {
}
// 账号注销
async requestAccountDeletion(password: string): Promise<import('../types/dto').DeletionStatusDTO | null> {
async requestAccountDeletion(password: string): Promise<DeletionStatusDTO | null> {
try {
const response = await api.post<import('../types/dto').DeletionStatusDTO>('/users/me/deactivate', {
const response = await api.post<DeletionStatusDTO>('/users/me/deactivate', {
password,
});
return response.data;
@@ -500,9 +503,9 @@ class AuthService {
}
}
async getDeletionStatus(): Promise<import('../types/dto').DeletionStatusDTO | null> {
async getDeletionStatus(): Promise<DeletionStatusDTO | null> {
try {
const response = await api.get<import('../types/dto').DeletionStatusDTO>('/users/me/deletion-status');
const response = await api.get<DeletionStatusDTO>('/users/me/deletion-status');
return response.data;
} catch (error) {
console.error('获取注销状态失败:', error);

View File

@@ -0,0 +1,18 @@
export { authService, resolveAuthApiError, qrcodeApi } from './authService';
export type {
LoginRequest,
RegisterRequest,
SendEmailCodeRequest,
ResetPasswordRequest,
VerifyCurrentUserEmailRequest,
AuthResponse,
RefreshTokenResponse,
UpdateUserRequest,
BlockedUserListResponse,
BlockStatusResponse,
QRCodeSession,
ScanResponse,
} from './authService';
export { verificationService } from './verificationService';
export type { SubmitVerificationResponse } from './verificationService';

View File

@@ -3,13 +3,13 @@
*
*/
import { api } from './api';
import { api } from '../core/api';
import {
VerificationStatusDTO,
VerificationRecordDTO,
SubmitVerificationRequest,
PaginatedData,
} from '../types/dto';
} from '@/types/dto';
// 提交认证响应
export interface SubmitVerificationResponse {

View File

@@ -7,7 +7,7 @@
import { AppState, AppStateStatus, Platform } from 'react-native';
import { ForegroundServiceModule } from './ForegroundServiceModule';
import { wsService } from './wsService';
import { wsService } from '../core/wsService';
import AsyncStorage from '@react-native-async-storage/async-storage';
/**

View File

@@ -15,8 +15,8 @@ import {
backgroundSyncManager,
BackgroundSyncMode,
} from './BackgroundSyncManager';
import { messageService } from './messageService';
import { api } from './api';
import { messageService } from '../message/messageService';
import { api } from '../core/api';
// 后台任务名称
const BACKGROUND_SYNC_TASK = 'background-sync-task';
@@ -215,7 +215,7 @@ export {
setVibrationConfig,
getVibrationConfig,
setVibrationEnabled,
} from './messageVibrationService';
} from '../platform/messageVibrationService';
// 重新导出类型
export { BackgroundSyncMode };
@@ -230,7 +230,7 @@ export const backgroundService = {
checkStatus: checkBackgroundStatus,
// 震动相关
vibrate: async () => {
const { triggerVibration } = await import('./messageVibrationService');
const { triggerVibration } = await import('../platform/messageVibrationService');
triggerVibration();
},
};

View File

@@ -0,0 +1,19 @@
export {
backgroundService,
initBackgroundService,
stopBackgroundService,
triggerVibration,
vibrateOnMessage,
setVibrationConfig,
getVibrationConfig,
setVibrationEnabled,
checkBackgroundStatus,
setBackgroundSyncMode,
getBackgroundSyncMode,
syncNow,
} from './backgroundService';
export { BackgroundSyncMode, backgroundSyncManager } from './BackgroundSyncManager';
export { ForegroundServiceModule } from './ForegroundServiceModule';
export type { ForegroundServiceOptions } from './ForegroundServiceModule';

View File

@@ -75,6 +75,7 @@ class ApiClient {
private baseUrl: string;
private verificationModalShown = false;
private refreshLock: Promise<boolean> | null = null;
private isAuthFailed = false; // 全局认证失败标志,防止无限重试
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
@@ -90,10 +91,17 @@ class ApiClient {
}
private navigateToLogin() {
this.isAuthFailed = true; // 设置全局失败标志
eventBus.emit({ type: 'NAVIGATE', payload: { path: '/(auth)/login' } });
eventBus.emit({ type: 'AUTH_LOGOUT' });
}
// 重置认证状态(登录成功后调用)
resetAuthState() {
this.isAuthFailed = false;
this.refreshLock = null;
}
private parseJwt(token: string): JwtPayload | null {
try {
const parts = token.split('.');
@@ -167,13 +175,22 @@ class ApiClient {
}
}
// 不需要认证的路径前缀
private static PUBLIC_PATHS = ['/auth/login', '/auth/register', '/auth/password/', '/auth/check-username', '/auth/qrcode', '/auth/refresh'];
// 统一请求方法
private async request<T>(
method: string,
path: string,
params?: Record<string, any>,
body?: any
body?: any,
_retryCount = 0,
): Promise<ApiResponse<T>> {
// 认证已失败,仅放行公开路径(登录、注册等)
if (this.isAuthFailed && !ApiClient.PUBLIC_PATHS.some(p => path.startsWith(p))) {
throw new ApiError(401, '登录已过期,请重新登录');
}
const url = new URL(`${this.baseUrl}${path}`);
// 添加查询参数
@@ -226,6 +243,11 @@ class ApiClient {
// 处理 401 未授权
if (response.status === 401) {
if (_retryCount >= 1) {
await this.clearToken();
this.navigateToLogin();
throw new ApiError(401, '登录已过期,请重新登录');
}
const refreshed = await this.refreshToken();
if (!refreshed) {
await this.clearToken();
@@ -233,7 +255,7 @@ class ApiClient {
throw new ApiError(401, '登录已过期,请重新登录');
}
return this.request(method, path, params, body);
return this.request(method, path, params, body, _retryCount + 1);
}
// 解析响应(兼容非 JSON 返回,避免 SyntaxError 被误判为网络错误)

View File

@@ -10,7 +10,7 @@
* console.error + Alert
*/
import { showPrompt } from './promptService';
import { showPrompt } from '../ui/promptService';
export enum AppErrorCode {
NETWORK_ERROR = 'NETWORK_ERROR',

View File

@@ -0,0 +1,39 @@
export { api, WS_URL, TOKEN_KEY, REFRESH_TOKEN_KEY } from './api';
export { ApiError } from './api';
export type { ApiResponse, PaginatedData } from './api';
export { wsService } from './wsService';
export type {
WSMessage,
WSMessageType,
WSChatMessage,
WSReadMessage,
WSTypingMessage,
WSRecallMessage,
WSNotificationMessage,
WSAnnouncementMessage,
WSGroupChatMessage,
WSGroupTypingMessage,
GroupNoticeType,
WSGroupNoticeMessage,
WSGroupMentionMessage,
WSGroupReadMessage,
WSGroupRecallMessage,
WSCallIncomingMessage,
WSCallAcceptedMessage,
WSCallRejectedMessage,
WSCallBusyMessage,
WSCallEndedMessage,
WSCallSDPMessage,
WSCallICEMessage,
WSCallPeerMutedMessage,
WSCallInvitedMessage,
WSCallAnsweredElsewhereMessage,
WSErrorMessage,
WSServerMessage,
ICEServerConfig,
} from './wsService';
export { handleError, createErrorHandler, safeAsync, createAppError } from './errorHandler';
export type { AppError, ErrorHandlerOptions } from './errorHandler';
export { AppErrorCode } from './errorHandler';

View File

@@ -2,8 +2,8 @@ import { AppState, AppStateStatus } from 'react-native';
import { api, WS_URL } from './api';
import { eventBus } from '@/core/events/EventBus';
import { MessageCategory, SystemMessageType, SystemMessageExtraData, MessageSegment } from '../types/dto';
import { systemNotificationService } from './systemNotificationService';
import { MessageCategory, SystemMessageType, SystemMessageExtraData, MessageSegment } from '@/types/dto';
import { systemNotificationService } from '../notification/systemNotificationService';
export type WSMessageType =
| 'chat'

View File

@@ -1,54 +1,6 @@
/**
* 服务导出
* 统一导出所有API服务
*/
// API 客户端
export { api, WS_URL, TOKEN_KEY, REFRESH_TOKEN_KEY } from './api';
export type { ApiResponse, PaginatedData, ApiError } from './api';
// 认证服务
export { authService } from './authService';
export { resolveAuthApiError } from './authService';
export type {
LoginRequest,
RegisterRequest,
SendEmailCodeRequest,
ResetPasswordRequest,
VerifyCurrentUserEmailRequest,
AuthResponse,
RefreshTokenResponse,
UpdateUserRequest,
BlockedUserListResponse,
BlockStatusResponse,
} from './authService';
// 帖子服务
export { postService } from './postService';
// 评论服务
export { commentService } from './commentService';
// 消息服务
export { messageService } from './messageService';
// 通知服务
export { notificationService } from './notificationService';
// 文件上传服务
export { uploadService } from './uploadService';
export type { UploadResponse } from './uploadService';
// 推送服务
export { pushService, registerDevice, getDevices, unregisterDevice, updateDeviceToken, getPushRecords } from './pushService';
// 投票服务
export { voteService } from './voteService';
export { channelService } from './channelService';
export type { ChannelItem } from './channelService';
// WebSocket 实时服务
export { wsService } from './wsService';
export { api, WS_URL, TOKEN_KEY, REFRESH_TOKEN_KEY, ApiError } from './core';
export type { ApiResponse, PaginatedData } from './core';
export { wsService } from './core';
export type {
WSMessage,
WSMessageType,
@@ -64,15 +16,32 @@ export type {
WSGroupNoticeMessage,
WSGroupMentionMessage,
WSGroupReadMessage,
WSGroupRecallMessage
} from './wsService';
WSGroupRecallMessage,
} from './core';
export { handleError, createErrorHandler, safeAsync, createAppError, AppErrorCode } from './core';
export type { AppError, ErrorHandlerOptions } from './core';
// 系统通知服务
export { systemNotificationService, getNotificationTitle } from './systemNotificationService';
export type { AppNotificationType } from './systemNotificationService';
export { authService, resolveAuthApiError } from './auth';
export type {
LoginRequest,
RegisterRequest,
SendEmailCodeRequest,
ResetPasswordRequest,
VerifyCurrentUserEmailRequest,
AuthResponse,
RefreshTokenResponse,
UpdateUserRequest,
BlockedUserListResponse,
BlockStatusResponse,
} from './auth';
export { verificationService } from './auth';
export type { SubmitVerificationResponse } from './auth';
// 群组服务
export { groupService } from './groupService';
export { postService, commentService, voteService, channelService, reportService } from './post';
export type { ChannelItem } from './post';
export { messageService } from './message';
export { groupService } from './message';
export type {
GroupResponse,
GroupMemberResponse,
@@ -88,13 +57,16 @@ export type {
SetMuteAllRequest,
SetJoinTypeRequest,
CreateAnnouncementRequest,
} from './groupService';
} from './message';
// 课表服务
export { scheduleService } from './scheduleService';
export type { CreateScheduleCourseRequest } from './scheduleService';
export { notificationService } from './notification';
export { systemNotificationService, getNotificationTitle } from './notification';
export type { AppNotificationType } from './notification';
export { pushService, registerDevice, getDevices, unregisterDevice, updateDeviceToken, getPushRecords } from './notification';
export { uploadService } from './upload';
export type { UploadResponse } from './upload';
// 后台保活服务
export {
backgroundService,
initBackgroundService,
@@ -105,21 +77,12 @@ export {
getVibrationConfig,
setVibrationEnabled,
checkBackgroundStatus,
} from './backgroundService';
} from './background';
// 统一提示/弹窗服务
export { showPrompt } from './promptService';
export { showConfirm } from './dialogService';
export { showPrompt } from './ui';
export { showConfirm } from './ui';
// 统一错误处理服务
export { handleError, createErrorHandler, safeAsync, createAppError } from './errorHandler';
export type { AppError, ErrorHandlerOptions } from './errorHandler';
export { AppErrorCode } from './errorHandler';
// APK 更新检查服务
export { apkUpdateService, checkForAPKUpdate, manualCheckForUpdate } from './apkUpdateService';
export type { APKVersionInfo, VersionCheckResult } from './apkUpdateService';
// 身份认证服务
export { verificationService } from './verificationService';
export type { SubmitVerificationResponse } from './verificationService';
export { apkUpdateService, checkForAPKUpdate, manualCheckForUpdate } from './platform';
export type { APKVersionInfo, VersionCheckResult } from './platform';
export { scheduleService } from './platform';
export type { CreateScheduleCourseRequest } from './platform';

View File

@@ -4,7 +4,7 @@
* 使 RESTful API: /api/v1/groups
*/
import { api } from './api';
import { api } from '../core/api';
import {
GroupResponse,
GroupMemberResponse,
@@ -25,7 +25,7 @@ import {
HandleGroupRequestAction,
CursorPaginationRequest,
CursorPaginationResponse,
} from '../types/dto';
} from '@/types/dto';
function asGroupId(raw: unknown): string {
return String(raw);

View File

@@ -0,0 +1,42 @@
export { messageService } from './messageService';
export type { RemoteConversationListPageResult } from './messageService';
export type {
ConversationListResponse,
ConversationResponse,
ConversationDetailResponse,
MessageListResponse,
MessageResponse,
SendMessageRequest,
UnreadCountResponse,
ConversationUnreadCountResponse,
SystemMessageListResponse,
SystemUnreadCountResponse,
} from './messageService';
export { groupService } from './groupService';
export type {
GroupResponse,
GroupMemberResponse,
GroupAnnouncementResponse,
GroupListResponse,
GroupMemberListResponse,
GroupAnnouncementListResponse,
CreateGroupRequest,
InviteMembersRequest,
TransferOwnerRequest,
SetRoleRequest,
SetNicknameRequest,
SetMuteAllRequest,
SetJoinTypeRequest,
CreateAnnouncementRequest,
} from './groupService';
export type { CustomSticker } from './stickerService';
export {
getCustomStickers,
addStickerFromUrl,
deleteSticker,
isStickerExists,
reorderStickers,
batchDeleteStickers,
} from './stickerService';

View File

@@ -4,8 +4,8 @@
* 使 API : /api/v1/conversations (RESTful action )
*/
import { api } from './api';
import { wsService } from './wsService';
import { api } from '../core/api';
import { wsService } from '../core/wsService';
import {
ConversationListResponse,
ConversationResponse,
@@ -21,7 +21,7 @@ import {
MessageSegment,
CursorPaginationRequest,
CursorPaginationResponse,
} from '../types/dto';
} from '@/types/dto';
import { conversationRepository, userCacheRepository } from '@/database';
/** 远端会话列表分页offset / cursor统一结果拉取成功后均已执行本地缓存同步 */

View File

@@ -3,8 +3,8 @@
*
*/
import { api } from './api';
import { uploadService } from './uploadService';
import { api } from '../core/api';
import { uploadService } from '../upload/uploadService';
// 自定义表情类型
export interface CustomSticker {

View File

@@ -0,0 +1,17 @@
export { notificationService } from './notificationService';
export { systemNotificationService, getNotificationTitle } from './systemNotificationService';
export type { AppNotificationType } from './systemNotificationService';
export {
NOTIFICATION_PREF_KEYS,
getNotificationPreferencesSync,
loadNotificationPreferences,
setVibrationPreference,
setPushNotificationsPreference,
setSoundPreference,
registerNotificationPresentationHandler,
} from './notificationPreferences';
export type { NotificationPrefs } from './notificationPreferences';
export { pushService, registerDevice, getDevices, unregisterDevice, updateDeviceToken, getPushRecords } from './pushService';

View File

@@ -5,7 +5,7 @@
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Notifications from 'expo-notifications';
import { setVibrationEnabled } from './messageVibrationService';
import { setVibrationEnabled } from '../platform/messageVibrationService';
export const NOTIFICATION_PREF_KEYS = {
/** 与历史版本保持一致 */

View File

@@ -3,9 +3,9 @@
*
*/
import { api, PaginatedData } from './api';
import { Notification, NotificationBadge, NotificationType } from '../types';
import { CursorPaginationRequest, CursorPaginationResponse } from '../types/dto';
import { api, PaginatedData } from '../core/api';
import { Notification, NotificationBadge, NotificationType } from '@/types';
import { CursorPaginationRequest, CursorPaginationResponse } from '@/types/dto';
// 通知列表响应
interface NotificationListResponse {

View File

@@ -2,12 +2,12 @@
* - Token和推送记录
*/
import { api } from './api';
import { api } from '../core/api';
import {
DeviceTokenResponse,
RegisterDeviceRequest,
PushRecordResponse
} from '../types/dto';
} from '@/types/dto';
// 推送服务类
class PushService {

View File

@@ -6,10 +6,10 @@
import * as Notifications from 'expo-notifications';
import { Platform, AppState, AppStateStatus } from 'react-native';
import type { WSChatMessage, WSNotificationMessage, WSAnnouncementMessage } from './wsService';
import { extractTextFromSegments } from '../types/dto';
import type { WSChatMessage, WSNotificationMessage, WSAnnouncementMessage } from '../core/wsService';
import { extractTextFromSegments } from '@/types/dto';
import { getNotificationPreferencesSync } from './notificationPreferences';
import { vibrateOnMessage } from './messageVibrationService';
import { vibrateOnMessage } from '../platform/messageVibrationService';
// 通知渠道配置
const CHANNEL_ID = 'default';

View File

@@ -6,7 +6,7 @@
import { Platform, Linking, Alert } from 'react-native';
import Constants from 'expo-constants';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { showConfirm } from './dialogService';
import { showConfirm } from '../ui/dialogService';
// 条件导入原生模块(仅在 Android 上可用)
// eslint-disable-next-line @typescript-eslint/no-var-requires

View File

@@ -0,0 +1,13 @@
export { apkUpdateService, checkForAPKUpdate, manualCheckForUpdate } from './apkUpdateService';
export type { APKVersionInfo, VersionCheckResult } from './apkUpdateService';
export { scheduleService } from './scheduleService';
export type { CreateScheduleCourseRequest, SyncScheduleRequest } from './scheduleService';
export {
triggerVibration,
vibrateOnMessage,
setVibrationConfig,
getVibrationConfig,
setVibrationEnabled,
} from './messageVibrationService';

View File

@@ -1,5 +1,5 @@
import { api } from './api';
import { Course } from '../types/schedule';
import { api } from '../core/api';
import { Course } from '@/types/schedule';
interface ScheduleCourseDTO {
id: string;

View File

@@ -1,4 +1,4 @@
import { api } from './api';
import { api } from '../core/api';
export interface ChannelItem {
id: string;

View File

@@ -3,9 +3,9 @@
*
*/
import { api, PaginatedData } from './api';
import { Comment, CreateCommentInput } from '../types';
import { CursorPaginationRequest, CursorPaginationResponse, CommentDTO } from '../types/dto';
import { api, PaginatedData } from '../core/api';
import { Comment, CreateCommentInput } from '@/types';
import { CursorPaginationRequest, CursorPaginationResponse, CommentDTO } from '@/types/dto';
// 评论列表响应
interface CommentListResponse {

View File

@@ -0,0 +1,15 @@
export { postService } from './postService';
export { commentService } from './commentService';
export { voteService } from './voteService';
export { channelService } from './channelService';
export type { ChannelItem } from './channelService';
export { reportService, REPORT_REASONS } from './reportService';
export type { ReportReason, ReportTargetType, ReportResponse } from './reportService';
export { postSyncService } from './PostSyncService';
export type { PostDetailState, PostsState } from './PostSyncService';
export { mergePostListWithFastPath, mergeRefreshWindow } from './postMerge';

View File

@@ -3,9 +3,9 @@
* 使 postSyncService
*/
import { api, PaginatedData } from './api';
import { Post, CreatePostInput } from '../types';
import { CursorPaginationRequest, CursorPaginationResponse } from '../types/dto';
import { api, PaginatedData } from '../core/api';
import { Post, CreatePostInput } from '@/types';
import { CursorPaginationRequest, CursorPaginationResponse } from '@/types/dto';
// 帖子列表响应
interface PostListResponse {

View File

@@ -3,7 +3,7 @@
*
*/
import { api } from './api';
import { api } from '../core/api';
// 举报原因类型
export type ReportReason = 'spam' | 'inappropriate' | 'harassment' | 'misinformation' | 'other';

View File

@@ -3,8 +3,8 @@
* API调用
*/
import { api } from './api';
import { Post, VoteResultDTO, CreateVotePostRequest } from '../types';
import { api } from '../core/api';
import { Post, VoteResultDTO, CreateVotePostRequest } from '@/types';
// 投票响应
interface VoteResponse {

16
src/services/ui/index.ts Normal file
View File

@@ -0,0 +1,16 @@
export {
showPrompt,
bindPromptListener,
PromptType,
PromptPayload,
} from './promptService';
export {
showDialog,
bindDialogListener,
showConfirm,
DialogPayload,
ConfirmOptions,
} from './dialogService';
export { installAlertOverride } from './alertOverride';

View File

@@ -0,0 +1,2 @@
export { uploadService } from './uploadService';
export type { UploadResponse } from './uploadService';

View File

@@ -3,7 +3,7 @@
*
*/
import { api } from './api';
import { api } from '../core/api';
/**
* URI mimeType