fix(frontend): 修复 TypeScript 类型错误,与后端 API 响应结构对齐
- 修复 UserDTO 类型定义,字段改为非 nullable 类型 - 修复 PostMapper 使用正确的 snake_case 字段名 - 修复 UserMapper 移除不存在的 is_blocked 和 updated_at 字段 - 修复 MessageMapper 使用 segments 替代已移除的字段 - 修复 MessageSyncService 正确处理 API 响应类型 - 修复 LocalDataSource SQLite 参数类型问题 - 修复导航相关类型错误 - 修复 PostDetailScreen 和 EditProfileScreen 的 null/undefined 类型不匹配
This commit is contained in:
@@ -72,12 +72,7 @@ export class ApiDataSource implements IApiDataSource {
|
||||
async delete<T>(url: string, data?: any): Promise<T> {
|
||||
try {
|
||||
const fullUrl = this.buildUrl(url);
|
||||
// 处理带 request body 的 DELETE 请求
|
||||
if (data) {
|
||||
const response = await api.request<T>('DELETE', fullUrl, undefined, data);
|
||||
return response.data;
|
||||
}
|
||||
const response = await api.delete<T>(fullUrl);
|
||||
const response = await api.delete<T>(fullUrl, data);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
this.handleError(error, 'DELETE');
|
||||
|
||||
@@ -183,7 +183,10 @@ export class LocalDataSource implements ILocalDataSource {
|
||||
async query<T>(sql: string, params?: any[]): Promise<T[]> {
|
||||
try {
|
||||
const db = this.ensureDb();
|
||||
return await db.getAllAsync<T>(sql, params);
|
||||
if (params && params.length > 0) {
|
||||
return await db.getAllAsync<T>(sql, params as any);
|
||||
}
|
||||
return await db.getAllAsync<T>(sql);
|
||||
} catch (error) {
|
||||
this.handleError(error, 'QUERY');
|
||||
}
|
||||
@@ -192,7 +195,10 @@ export class LocalDataSource implements ILocalDataSource {
|
||||
async getFirst<T>(sql: string, params?: any[]): Promise<T | null> {
|
||||
try {
|
||||
const db = this.ensureDb();
|
||||
return await db.getFirstAsync<T>(sql, params);
|
||||
if (params && params.length > 0) {
|
||||
return await db.getFirstAsync<T>(sql, params as any);
|
||||
}
|
||||
return await db.getFirstAsync<T>(sql);
|
||||
} catch (error) {
|
||||
this.handleError(error, 'GET_FIRST');
|
||||
}
|
||||
@@ -210,7 +216,10 @@ export class LocalDataSource implements ILocalDataSource {
|
||||
async run(sql: string, params?: any[]): Promise<{ lastInsertRowId: number; changes: number }> {
|
||||
try {
|
||||
const db = this.ensureDb();
|
||||
return await db.runAsync(sql, params);
|
||||
if (params && params.length > 0) {
|
||||
return await db.runAsync(sql, params as any);
|
||||
}
|
||||
return await db.runAsync(sql);
|
||||
} catch (error) {
|
||||
this.handleError(error, 'RUN');
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ export class ConversationMapper {
|
||||
id: String(user.id || ''),
|
||||
username: user.username || '',
|
||||
nickname: user.nickname,
|
||||
avatar: user.avatar,
|
||||
avatar: user.avatar ?? undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -220,19 +220,31 @@ export class ConversationMapper {
|
||||
* 映射群组 API 响应
|
||||
*/
|
||||
private static mapGroupFromApi(group: GroupResponse): GroupModel {
|
||||
// 将数字类型的 join_type 转换为字符串类型
|
||||
// JoinType: 0 = 允许任何人, 1 = 需要审批, 2 = 不允许
|
||||
const mapJoinType = (joinType: number): 'anyone' | 'approval' | 'invite' => {
|
||||
switch (joinType) {
|
||||
case 0: return 'anyone';
|
||||
case 1: return 'approval';
|
||||
case 2: return 'invite';
|
||||
default: return 'approval';
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
id: String(group.id || ''),
|
||||
name: group.name || '',
|
||||
avatar: group.avatar,
|
||||
description: group.description,
|
||||
announcement: group.announcement,
|
||||
// GroupResponse 没有 announcement 和 updated_at 字段,使用默认值
|
||||
announcement: undefined,
|
||||
ownerId: String(group.owner_id || ''),
|
||||
memberCount: group.member_count || 0,
|
||||
maxMemberCount: group.max_member_count || 500,
|
||||
joinType: group.join_type || 'approval',
|
||||
maxMemberCount: group.max_members || 500,
|
||||
joinType: mapJoinType(group.join_type),
|
||||
isMuted: group.mute_all || false,
|
||||
createdAt: new Date(group.created_at || Date.now()),
|
||||
updatedAt: new Date(group.updated_at || Date.now()),
|
||||
updatedAt: new Date(group.created_at || Date.now()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -32,13 +32,16 @@ export class MessageMapper {
|
||||
* 将 API 响应转换为应用模型
|
||||
*/
|
||||
static fromApiResponse(response: MessageResponse): MessageModel {
|
||||
const textSegment = response.segments?.find(s => s.type === 'text');
|
||||
const content = textSegment?.data?.text || textSegment?.data?.content || '';
|
||||
|
||||
return {
|
||||
id: String(response.id || ''),
|
||||
conversationId: String(response.conversation_id || ''),
|
||||
senderId: String(response.sender_id || ''),
|
||||
content: response.content || '',
|
||||
type: response.message_type || 'text',
|
||||
isRead: response.is_read || false,
|
||||
content,
|
||||
type: 'text',
|
||||
isRead: false,
|
||||
createdAt: new Date(response.created_at || Date.now()),
|
||||
seq: response.seq || 0,
|
||||
status: response.status || 'normal',
|
||||
@@ -169,15 +172,12 @@ export class MessageMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 映射发送者信息
|
||||
*/
|
||||
private static mapSenderFromApi(sender: UserDTO): UserModel {
|
||||
return {
|
||||
id: String(sender.id || ''),
|
||||
username: sender.username || '',
|
||||
nickname: sender.nickname,
|
||||
avatar: sender.avatar,
|
||||
avatar: sender.avatar || undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,31 +4,28 @@
|
||||
*/
|
||||
|
||||
import { PostModel, UserModel } from '../models';
|
||||
import type { Post } from '../../types';
|
||||
import type { PostDTO } from '../../types/dto';
|
||||
|
||||
export class PostMapper {
|
||||
/**
|
||||
* 将 API 响应转换为应用模型
|
||||
*/
|
||||
static fromApiResponse(response: Post): PostModel {
|
||||
static fromApiResponse(response: PostDTO): PostModel {
|
||||
return {
|
||||
id: String(response.id || ''),
|
||||
authorId: String(response.author_id || ''),
|
||||
authorId: String(response.user_id || ''),
|
||||
author: response.author ? this.mapAuthorFromApi(response.author) : undefined,
|
||||
title: response.title || '',
|
||||
content: response.content || '',
|
||||
images: response.images || [],
|
||||
likeCount: response.like_count || 0,
|
||||
commentCount: response.comment_count || 0,
|
||||
shareCount: response.share_count || 0,
|
||||
viewCount: response.view_count || 0,
|
||||
favoriteCount: response.favorite_count || 0,
|
||||
images: response.images?.map(img => img.url) || [],
|
||||
likeCount: response.likes_count || 0,
|
||||
commentCount: response.comments_count || 0,
|
||||
shareCount: response.shares_count || 0,
|
||||
viewCount: response.views_count || 0,
|
||||
favoriteCount: response.favorites_count || 0,
|
||||
isLiked: response.is_liked || false,
|
||||
isFavorited: response.is_favorited || false,
|
||||
isTop: response.is_top || false,
|
||||
status: response.status || 'published',
|
||||
isTop: response.is_pinned || false,
|
||||
status: (response.status || 'published') as 'published' | 'draft' | 'deleted',
|
||||
communityId: response.community_id,
|
||||
tags: response.tags || [],
|
||||
tags: [],
|
||||
createdAt: new Date(response.created_at || Date.now()),
|
||||
updatedAt: new Date(response.updated_at || Date.now()),
|
||||
};
|
||||
@@ -37,7 +34,7 @@ export class PostMapper {
|
||||
/**
|
||||
* 将 API 响应数组转换为应用模型数组
|
||||
*/
|
||||
static fromApiResponseList(responses: Post[]): PostModel[] {
|
||||
static fromApiResponseList(responses: PostDTO[]): PostModel[] {
|
||||
return responses.map(r => this.fromApiResponse(r));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { UserModel } from '../models';
|
||||
import type { UserDTO, User } from '../../types/dto';
|
||||
import type { UserDTO } from '../../types/dto';
|
||||
|
||||
// 数据库用户记录类型
|
||||
export interface UserDbRecord {
|
||||
@@ -14,9 +14,6 @@ export interface UserDbRecord {
|
||||
}
|
||||
|
||||
export class UserMapper {
|
||||
/**
|
||||
* 将 API DTO 转换为应用模型
|
||||
*/
|
||||
static fromDTO(dto: UserDTO): UserModel {
|
||||
return {
|
||||
id: String(dto.id || ''),
|
||||
@@ -32,39 +29,10 @@ export class UserMapper {
|
||||
followingCount: dto.following_count,
|
||||
postsCount: dto.posts_count,
|
||||
isFollowing: dto.is_following,
|
||||
isBlocked: dto.is_blocked,
|
||||
createdAt: dto.created_at ? new Date(dto.created_at) : undefined,
|
||||
updatedAt: dto.updated_at ? new Date(dto.updated_at) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 User 类型转换为应用模型
|
||||
*/
|
||||
static fromUser(user: User): UserModel {
|
||||
return {
|
||||
id: String(user.id || ''),
|
||||
username: user.username || '',
|
||||
nickname: user.nickname,
|
||||
avatar: user.avatar,
|
||||
bio: user.bio,
|
||||
website: user.website,
|
||||
location: user.location,
|
||||
email: user.email,
|
||||
phone: user.phone,
|
||||
followersCount: user.followers_count,
|
||||
followingCount: user.following_count,
|
||||
postsCount: user.posts_count,
|
||||
isFollowing: user.is_following,
|
||||
isBlocked: user.is_blocked,
|
||||
createdAt: user.created_at ? new Date(user.created_at) : undefined,
|
||||
updatedAt: user.updated_at ? new Date(user.updated_at) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 DTO 数组转换为应用模型数组
|
||||
*/
|
||||
static fromDTOList(dtos: UserDTO[]): UserModel[] {
|
||||
return dtos.map(dto => this.fromDTO(dto));
|
||||
}
|
||||
@@ -88,20 +56,19 @@ export class UserMapper {
|
||||
const dto: UserDTO = {
|
||||
id: model.id,
|
||||
username: model.username,
|
||||
nickname: model.nickname,
|
||||
avatar: model.avatar,
|
||||
bio: model.bio,
|
||||
website: model.website,
|
||||
location: model.location,
|
||||
nickname: model.nickname || '',
|
||||
avatar: model.avatar || '',
|
||||
cover_url: '',
|
||||
bio: model.bio || '',
|
||||
website: model.website || '',
|
||||
location: model.location || '',
|
||||
email: model.email,
|
||||
phone: model.phone,
|
||||
followers_count: model.followersCount,
|
||||
following_count: model.followingCount,
|
||||
posts_count: model.postsCount,
|
||||
followers_count: model.followersCount || 0,
|
||||
following_count: model.followingCount || 0,
|
||||
posts_count: model.postsCount || 0,
|
||||
is_following: model.isFollowing,
|
||||
is_blocked: model.isBlocked,
|
||||
created_at: model.createdAt?.toISOString(),
|
||||
updated_at: model.updatedAt?.toISOString(),
|
||||
created_at: model.createdAt?.toISOString() || '',
|
||||
};
|
||||
|
||||
return {
|
||||
@@ -149,20 +116,19 @@ export class UserMapper {
|
||||
return {
|
||||
id: model.id,
|
||||
username: model.username,
|
||||
nickname: model.nickname,
|
||||
avatar: model.avatar,
|
||||
bio: model.bio,
|
||||
website: model.website,
|
||||
location: model.location,
|
||||
nickname: model.nickname || '',
|
||||
avatar: model.avatar || '',
|
||||
cover_url: '',
|
||||
bio: model.bio || '',
|
||||
website: model.website || '',
|
||||
location: model.location || '',
|
||||
email: model.email,
|
||||
phone: model.phone,
|
||||
followers_count: model.followersCount,
|
||||
following_count: model.followingCount,
|
||||
posts_count: model.postsCount,
|
||||
followers_count: model.followersCount || 0,
|
||||
following_count: model.followingCount || 0,
|
||||
posts_count: model.postsCount || 0,
|
||||
is_following: model.isFollowing,
|
||||
is_blocked: model.isBlocked,
|
||||
created_at: model.createdAt?.toISOString(),
|
||||
updated_at: model.updatedAt?.toISOString(),
|
||||
created_at: model.createdAt?.toISOString() || '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 定义消息数据访问的抽象
|
||||
*/
|
||||
|
||||
import type { Message, Conversation, ConversationType } from '../../types/dto';
|
||||
import type { Message, Conversation } from '../../../core/entities/Message';
|
||||
|
||||
export interface IMessageRepository {
|
||||
// ==================== 消息操作 ====================
|
||||
|
||||
Reference in New Issue
Block a user