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> {
|
async delete<T>(url: string, data?: any): Promise<T> {
|
||||||
try {
|
try {
|
||||||
const fullUrl = this.buildUrl(url);
|
const fullUrl = this.buildUrl(url);
|
||||||
// 处理带 request body 的 DELETE 请求
|
const response = await api.delete<T>(fullUrl, data);
|
||||||
if (data) {
|
|
||||||
const response = await api.request<T>('DELETE', fullUrl, undefined, data);
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
const response = await api.delete<T>(fullUrl);
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleError(error, 'DELETE');
|
this.handleError(error, 'DELETE');
|
||||||
|
|||||||
@@ -183,7 +183,10 @@ export class LocalDataSource implements ILocalDataSource {
|
|||||||
async query<T>(sql: string, params?: any[]): Promise<T[]> {
|
async query<T>(sql: string, params?: any[]): Promise<T[]> {
|
||||||
try {
|
try {
|
||||||
const db = this.ensureDb();
|
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) {
|
} catch (error) {
|
||||||
this.handleError(error, 'QUERY');
|
this.handleError(error, 'QUERY');
|
||||||
}
|
}
|
||||||
@@ -192,7 +195,10 @@ export class LocalDataSource implements ILocalDataSource {
|
|||||||
async getFirst<T>(sql: string, params?: any[]): Promise<T | null> {
|
async getFirst<T>(sql: string, params?: any[]): Promise<T | null> {
|
||||||
try {
|
try {
|
||||||
const db = this.ensureDb();
|
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) {
|
} catch (error) {
|
||||||
this.handleError(error, 'GET_FIRST');
|
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 }> {
|
async run(sql: string, params?: any[]): Promise<{ lastInsertRowId: number; changes: number }> {
|
||||||
try {
|
try {
|
||||||
const db = this.ensureDb();
|
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) {
|
} catch (error) {
|
||||||
this.handleError(error, 'RUN');
|
this.handleError(error, 'RUN');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ export class ConversationMapper {
|
|||||||
id: String(user.id || ''),
|
id: String(user.id || ''),
|
||||||
username: user.username || '',
|
username: user.username || '',
|
||||||
nickname: user.nickname,
|
nickname: user.nickname,
|
||||||
avatar: user.avatar,
|
avatar: user.avatar ?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,19 +220,31 @@ export class ConversationMapper {
|
|||||||
* 映射群组 API 响应
|
* 映射群组 API 响应
|
||||||
*/
|
*/
|
||||||
private static mapGroupFromApi(group: GroupResponse): GroupModel {
|
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 {
|
return {
|
||||||
id: String(group.id || ''),
|
id: String(group.id || ''),
|
||||||
name: group.name || '',
|
name: group.name || '',
|
||||||
avatar: group.avatar,
|
avatar: group.avatar,
|
||||||
description: group.description,
|
description: group.description,
|
||||||
announcement: group.announcement,
|
// GroupResponse 没有 announcement 和 updated_at 字段,使用默认值
|
||||||
|
announcement: undefined,
|
||||||
ownerId: String(group.owner_id || ''),
|
ownerId: String(group.owner_id || ''),
|
||||||
memberCount: group.member_count || 0,
|
memberCount: group.member_count || 0,
|
||||||
maxMemberCount: group.max_member_count || 500,
|
maxMemberCount: group.max_members || 500,
|
||||||
joinType: group.join_type || 'approval',
|
joinType: mapJoinType(group.join_type),
|
||||||
isMuted: group.mute_all || false,
|
isMuted: group.mute_all || false,
|
||||||
createdAt: new Date(group.created_at || Date.now()),
|
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 响应转换为应用模型
|
* 将 API 响应转换为应用模型
|
||||||
*/
|
*/
|
||||||
static fromApiResponse(response: MessageResponse): MessageModel {
|
static fromApiResponse(response: MessageResponse): MessageModel {
|
||||||
|
const textSegment = response.segments?.find(s => s.type === 'text');
|
||||||
|
const content = textSegment?.data?.text || textSegment?.data?.content || '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: String(response.id || ''),
|
id: String(response.id || ''),
|
||||||
conversationId: String(response.conversation_id || ''),
|
conversationId: String(response.conversation_id || ''),
|
||||||
senderId: String(response.sender_id || ''),
|
senderId: String(response.sender_id || ''),
|
||||||
content: response.content || '',
|
content,
|
||||||
type: response.message_type || 'text',
|
type: 'text',
|
||||||
isRead: response.is_read || false,
|
isRead: false,
|
||||||
createdAt: new Date(response.created_at || Date.now()),
|
createdAt: new Date(response.created_at || Date.now()),
|
||||||
seq: response.seq || 0,
|
seq: response.seq || 0,
|
||||||
status: response.status || 'normal',
|
status: response.status || 'normal',
|
||||||
@@ -169,15 +172,12 @@ export class MessageMapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 映射发送者信息
|
|
||||||
*/
|
|
||||||
private static mapSenderFromApi(sender: UserDTO): UserModel {
|
private static mapSenderFromApi(sender: UserDTO): UserModel {
|
||||||
return {
|
return {
|
||||||
id: String(sender.id || ''),
|
id: String(sender.id || ''),
|
||||||
username: sender.username || '',
|
username: sender.username || '',
|
||||||
nickname: sender.nickname,
|
nickname: sender.nickname,
|
||||||
avatar: sender.avatar,
|
avatar: sender.avatar || undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,31 +4,28 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { PostModel, UserModel } from '../models';
|
import { PostModel, UserModel } from '../models';
|
||||||
import type { Post } from '../../types';
|
import type { PostDTO } from '../../types/dto';
|
||||||
|
|
||||||
export class PostMapper {
|
export class PostMapper {
|
||||||
/**
|
static fromApiResponse(response: PostDTO): PostModel {
|
||||||
* 将 API 响应转换为应用模型
|
|
||||||
*/
|
|
||||||
static fromApiResponse(response: Post): PostModel {
|
|
||||||
return {
|
return {
|
||||||
id: String(response.id || ''),
|
id: String(response.id || ''),
|
||||||
authorId: String(response.author_id || ''),
|
authorId: String(response.user_id || ''),
|
||||||
author: response.author ? this.mapAuthorFromApi(response.author) : undefined,
|
author: response.author ? this.mapAuthorFromApi(response.author) : undefined,
|
||||||
title: response.title || '',
|
title: response.title || '',
|
||||||
content: response.content || '',
|
content: response.content || '',
|
||||||
images: response.images || [],
|
images: response.images?.map(img => img.url) || [],
|
||||||
likeCount: response.like_count || 0,
|
likeCount: response.likes_count || 0,
|
||||||
commentCount: response.comment_count || 0,
|
commentCount: response.comments_count || 0,
|
||||||
shareCount: response.share_count || 0,
|
shareCount: response.shares_count || 0,
|
||||||
viewCount: response.view_count || 0,
|
viewCount: response.views_count || 0,
|
||||||
favoriteCount: response.favorite_count || 0,
|
favoriteCount: response.favorites_count || 0,
|
||||||
isLiked: response.is_liked || false,
|
isLiked: response.is_liked || false,
|
||||||
isFavorited: response.is_favorited || false,
|
isFavorited: response.is_favorited || false,
|
||||||
isTop: response.is_top || false,
|
isTop: response.is_pinned || false,
|
||||||
status: response.status || 'published',
|
status: (response.status || 'published') as 'published' | 'draft' | 'deleted',
|
||||||
communityId: response.community_id,
|
communityId: response.community_id,
|
||||||
tags: response.tags || [],
|
tags: [],
|
||||||
createdAt: new Date(response.created_at || Date.now()),
|
createdAt: new Date(response.created_at || Date.now()),
|
||||||
updatedAt: new Date(response.updated_at || Date.now()),
|
updatedAt: new Date(response.updated_at || Date.now()),
|
||||||
};
|
};
|
||||||
@@ -37,7 +34,7 @@ export class PostMapper {
|
|||||||
/**
|
/**
|
||||||
* 将 API 响应数组转换为应用模型数组
|
* 将 API 响应数组转换为应用模型数组
|
||||||
*/
|
*/
|
||||||
static fromApiResponseList(responses: Post[]): PostModel[] {
|
static fromApiResponseList(responses: PostDTO[]): PostModel[] {
|
||||||
return responses.map(r => this.fromApiResponse(r));
|
return responses.map(r => this.fromApiResponse(r));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { UserModel } from '../models';
|
import { UserModel } from '../models';
|
||||||
import type { UserDTO, User } from '../../types/dto';
|
import type { UserDTO } from '../../types/dto';
|
||||||
|
|
||||||
// 数据库用户记录类型
|
// 数据库用户记录类型
|
||||||
export interface UserDbRecord {
|
export interface UserDbRecord {
|
||||||
@@ -14,9 +14,6 @@ export interface UserDbRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UserMapper {
|
export class UserMapper {
|
||||||
/**
|
|
||||||
* 将 API DTO 转换为应用模型
|
|
||||||
*/
|
|
||||||
static fromDTO(dto: UserDTO): UserModel {
|
static fromDTO(dto: UserDTO): UserModel {
|
||||||
return {
|
return {
|
||||||
id: String(dto.id || ''),
|
id: String(dto.id || ''),
|
||||||
@@ -32,39 +29,10 @@ export class UserMapper {
|
|||||||
followingCount: dto.following_count,
|
followingCount: dto.following_count,
|
||||||
postsCount: dto.posts_count,
|
postsCount: dto.posts_count,
|
||||||
isFollowing: dto.is_following,
|
isFollowing: dto.is_following,
|
||||||
isBlocked: dto.is_blocked,
|
|
||||||
createdAt: dto.created_at ? new Date(dto.created_at) : undefined,
|
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[] {
|
static fromDTOList(dtos: UserDTO[]): UserModel[] {
|
||||||
return dtos.map(dto => this.fromDTO(dto));
|
return dtos.map(dto => this.fromDTO(dto));
|
||||||
}
|
}
|
||||||
@@ -88,20 +56,19 @@ export class UserMapper {
|
|||||||
const dto: UserDTO = {
|
const dto: UserDTO = {
|
||||||
id: model.id,
|
id: model.id,
|
||||||
username: model.username,
|
username: model.username,
|
||||||
nickname: model.nickname,
|
nickname: model.nickname || '',
|
||||||
avatar: model.avatar,
|
avatar: model.avatar || '',
|
||||||
bio: model.bio,
|
cover_url: '',
|
||||||
website: model.website,
|
bio: model.bio || '',
|
||||||
location: model.location,
|
website: model.website || '',
|
||||||
|
location: model.location || '',
|
||||||
email: model.email,
|
email: model.email,
|
||||||
phone: model.phone,
|
phone: model.phone,
|
||||||
followers_count: model.followersCount,
|
followers_count: model.followersCount || 0,
|
||||||
following_count: model.followingCount,
|
following_count: model.followingCount || 0,
|
||||||
posts_count: model.postsCount,
|
posts_count: model.postsCount || 0,
|
||||||
is_following: model.isFollowing,
|
is_following: model.isFollowing,
|
||||||
is_blocked: model.isBlocked,
|
created_at: model.createdAt?.toISOString() || '',
|
||||||
created_at: model.createdAt?.toISOString(),
|
|
||||||
updated_at: model.updatedAt?.toISOString(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -149,20 +116,19 @@ export class UserMapper {
|
|||||||
return {
|
return {
|
||||||
id: model.id,
|
id: model.id,
|
||||||
username: model.username,
|
username: model.username,
|
||||||
nickname: model.nickname,
|
nickname: model.nickname || '',
|
||||||
avatar: model.avatar,
|
avatar: model.avatar || '',
|
||||||
bio: model.bio,
|
cover_url: '',
|
||||||
website: model.website,
|
bio: model.bio || '',
|
||||||
location: model.location,
|
website: model.website || '',
|
||||||
|
location: model.location || '',
|
||||||
email: model.email,
|
email: model.email,
|
||||||
phone: model.phone,
|
phone: model.phone,
|
||||||
followers_count: model.followersCount,
|
followers_count: model.followersCount || 0,
|
||||||
following_count: model.followingCount,
|
following_count: model.followingCount || 0,
|
||||||
posts_count: model.postsCount,
|
posts_count: model.postsCount || 0,
|
||||||
is_following: model.isFollowing,
|
is_following: model.isFollowing,
|
||||||
is_blocked: model.isBlocked,
|
created_at: model.createdAt?.toISOString() || '',
|
||||||
created_at: model.createdAt?.toISOString(),
|
|
||||||
updated_at: model.updatedAt?.toISOString(),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* 定义消息数据访问的抽象
|
* 定义消息数据访问的抽象
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Message, Conversation, ConversationType } from '../../types/dto';
|
import type { Message, Conversation } from '../../../core/entities/Message';
|
||||||
|
|
||||||
export interface IMessageRepository {
|
export interface IMessageRepository {
|
||||||
// ==================== 消息操作 ====================
|
// ==================== 消息操作 ====================
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class NavigationService {
|
|||||||
if (!this.isNavigationReady()) {
|
if (!this.isNavigationReady()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.navigationRef!.getCurrentRoute();
|
return this.navigationRef!.getCurrentRoute() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ export function DesktopNavigator({ unreadCount = 0 }: DesktopNavigatorProps) {
|
|||||||
>
|
>
|
||||||
<View style={styles.sidebarIconContainer}>
|
<View style={styles.sidebarIconContainer}>
|
||||||
<MaterialCommunityIcons
|
<MaterialCommunityIcons
|
||||||
name={isActive ? item.icon : item.iconOutline}
|
name={isActive ? item.icon as any : item.iconOutline as any}
|
||||||
size={24}
|
size={24}
|
||||||
color={isActive ? colors.primary.main : colors.text.secondary}
|
color={isActive ? colors.primary.main : colors.text.secondary}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export type {
|
|||||||
NavItemConfig,
|
NavItemConfig,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
// 导航器
|
|
||||||
export { AuthNavigator } from './AuthNavigator';
|
export { AuthNavigator } from './AuthNavigator';
|
||||||
export { HomeNavigator } from './HomeNavigator';
|
export { HomeNavigator } from './HomeNavigator';
|
||||||
export { MessageNavigator } from './MessageNavigator';
|
export { MessageNavigator } from './MessageNavigator';
|
||||||
@@ -24,7 +23,7 @@ export { ProfileNavigator } from './ProfileNavigator';
|
|||||||
export { TabNavigator } from './TabNavigator';
|
export { TabNavigator } from './TabNavigator';
|
||||||
export { DesktopNavigator } from './DesktopNavigator';
|
export { DesktopNavigator } from './DesktopNavigator';
|
||||||
export { RootNavigator } from './RootNavigator';
|
export { RootNavigator } from './RootNavigator';
|
||||||
export { MainNavigator } from './MainNavigator';
|
export { default as MainNavigator } from './MainNavigator';
|
||||||
|
|
||||||
// 移动端简化导航
|
// 移动端简化导航
|
||||||
export { SimpleMobileTabNavigator } from './SimpleMobileTabNavigator';
|
export { SimpleMobileTabNavigator } from './SimpleMobileTabNavigator';
|
||||||
|
|||||||
@@ -635,13 +635,13 @@ export const PostDetailScreen: React.FC = () => {
|
|||||||
id: '',
|
id: '',
|
||||||
username: 'guest',
|
username: 'guest',
|
||||||
nickname: '游客',
|
nickname: '游客',
|
||||||
avatar: null,
|
avatar: '',
|
||||||
cover_url: null,
|
cover_url: '',
|
||||||
bio: null,
|
bio: '',
|
||||||
website: null,
|
website: '',
|
||||||
location: null,
|
location: '',
|
||||||
phone: null,
|
phone: undefined,
|
||||||
email: null,
|
email: undefined,
|
||||||
posts_count: 0,
|
posts_count: 0,
|
||||||
followers_count: 0,
|
followers_count: 0,
|
||||||
following_count: 0,
|
following_count: 0,
|
||||||
|
|||||||
@@ -225,11 +225,11 @@ export const EditProfileScreen: React.FC = () => {
|
|||||||
if (updatedUser) {
|
if (updatedUser) {
|
||||||
updateUser({
|
updateUser({
|
||||||
nickname: nickname.trim(),
|
nickname: nickname.trim(),
|
||||||
bio: bio.trim() || null,
|
bio: bio.trim() || undefined,
|
||||||
location: location.trim() || null,
|
location: location.trim() || undefined,
|
||||||
website: website.trim() || null,
|
website: website.trim() || undefined,
|
||||||
phone: phone.trim() || null,
|
phone: phone.trim() || undefined,
|
||||||
email: email.trim() || null,
|
email: email.trim() || undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
Alert.alert('成功', '资料已更新', [
|
Alert.alert('成功', '资料已更新', [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { messageService } from '../../services/messageService';
|
import { messageService } from '../../services/messageService';
|
||||||
import { messageRepository } from '../../data/repositories/MessageRepository';
|
import { messageRepository } from '../../data/repositories/MessageRepository';
|
||||||
import type { Message, Conversation } from '../../core/entities/Message';
|
import type { Message, Conversation } from '../../core/entities/Message';
|
||||||
import type { ConversationResponse, MessageResponse } from '../../types/dto';
|
import type { ConversationListResponse, MessageListResponse } from '../../types/dto';
|
||||||
|
|
||||||
export interface SyncOptions {
|
export interface SyncOptions {
|
||||||
force?: boolean;
|
force?: boolean;
|
||||||
@@ -32,8 +32,8 @@ export class MessageSyncService {
|
|||||||
this.syncingConversations = true;
|
this.syncingConversations = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await messageService.getConversations();
|
const response: ConversationListResponse = await messageService.getConversations();
|
||||||
const conversations = this.mapConversations(response);
|
const conversations = this.mapConversations(response.list || []);
|
||||||
return conversations;
|
return conversations;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[MessageSyncService] Failed to sync conversations:', error);
|
console.error('[MessageSyncService] Failed to sync conversations:', error);
|
||||||
@@ -59,10 +59,12 @@ export class MessageSyncService {
|
|||||||
try {
|
try {
|
||||||
const lastSeq = options.lastSeq ?? await messageRepository.getMaxSeq(conversationId);
|
const lastSeq = options.lastSeq ?? await messageRepository.getMaxSeq(conversationId);
|
||||||
|
|
||||||
const response = await messageService.getMessages(conversationId, {
|
const response: MessageListResponse = await messageService.getMessages(
|
||||||
after_seq: lastSeq,
|
conversationId,
|
||||||
limit: 50,
|
lastSeq,
|
||||||
});
|
undefined,
|
||||||
|
50
|
||||||
|
);
|
||||||
|
|
||||||
const messages = this.mapMessages(response.messages || []);
|
const messages = this.mapMessages(response.messages || []);
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ export class MessageSyncService {
|
|||||||
return {
|
return {
|
||||||
conversations: [],
|
conversations: [],
|
||||||
messages,
|
messages,
|
||||||
hasMore: response.has_more || false,
|
hasMore: messages.length >= 50,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[MessageSyncService] Failed to sync messages:', error);
|
console.error('[MessageSyncService] Failed to sync messages:', error);
|
||||||
@@ -89,10 +91,12 @@ export class MessageSyncService {
|
|||||||
limit: number = 20
|
limit: number = 20
|
||||||
): Promise<Message[]> {
|
): Promise<Message[]> {
|
||||||
try {
|
try {
|
||||||
const response = await messageService.getMessages(conversationId, {
|
const response: MessageListResponse = await messageService.getMessages(
|
||||||
before_seq: beforeSeq,
|
conversationId,
|
||||||
limit,
|
undefined,
|
||||||
});
|
beforeSeq,
|
||||||
|
limit
|
||||||
|
);
|
||||||
|
|
||||||
const messages = this.mapMessages(response.messages || []);
|
const messages = this.mapMessages(response.messages || []);
|
||||||
|
|
||||||
@@ -122,31 +126,45 @@ export class MessageSyncService {
|
|||||||
return messageRepository.getMessagesBeforeSeq(conversationId, beforeSeq, limit);
|
return messageRepository.getMessagesBeforeSeq(conversationId, beforeSeq, limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapConversations(response: ConversationResponse[]): Conversation[] {
|
private mapConversations(list: ConversationListResponse['list']): Conversation[] {
|
||||||
return response.map(conv => ({
|
return list.map(conv => ({
|
||||||
id: conv.id,
|
id: conv.id,
|
||||||
type: conv.type || 'private',
|
type: conv.type || 'private',
|
||||||
isPinned: conv.isPinned || false,
|
isPinned: conv.is_pinned || false,
|
||||||
lastSeq: conv.lastSeq || conv.last_seq || 0,
|
lastSeq: conv.last_seq || 0,
|
||||||
lastMessageAt: conv.lastMessageAt || conv.last_message_at || new Date().toISOString(),
|
lastMessageAt: conv.last_message_at || new Date().toISOString(),
|
||||||
unreadCount: conv.unreadCount || conv.unread_count || 0,
|
unreadCount: conv.unread_count || 0,
|
||||||
participants: conv.participants || [],
|
participants: (conv.participants || []).map(p => ({
|
||||||
group: conv.group,
|
id: p.id,
|
||||||
createdAt: conv.createdAt || conv.created_at || new Date().toISOString(),
|
username: p.username,
|
||||||
updatedAt: conv.updatedAt || conv.updated_at || new Date().toISOString(),
|
avatar: p.avatar || undefined,
|
||||||
|
nickname: p.nickname,
|
||||||
|
})),
|
||||||
|
group: conv.group ? {
|
||||||
|
id: String(conv.group.id),
|
||||||
|
name: conv.group.name,
|
||||||
|
avatar: conv.group.avatar,
|
||||||
|
} : undefined,
|
||||||
|
createdAt: conv.created_at || new Date().toISOString(),
|
||||||
|
updatedAt: conv.updated_at || new Date().toISOString(),
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapMessages(response: MessageResponse[]): Message[] {
|
private mapMessages(messages: MessageListResponse['messages']): Message[] {
|
||||||
return response.map(msg => ({
|
return messages.map(msg => ({
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
conversationId: msg.conversationId || msg.conversation_id || '',
|
conversationId: msg.conversation_id,
|
||||||
senderId: msg.senderId || msg.sender_id || '',
|
senderId: msg.sender_id,
|
||||||
seq: msg.seq || 0,
|
seq: msg.seq,
|
||||||
segments: msg.segments || [],
|
segments: msg.segments || [],
|
||||||
createdAt: msg.createdAt || msg.created_at || new Date().toISOString(),
|
createdAt: msg.created_at,
|
||||||
status: msg.status || 'normal',
|
status: msg.status || 'normal',
|
||||||
sender: msg.sender,
|
sender: msg.sender ? {
|
||||||
|
id: msg.sender.id,
|
||||||
|
username: msg.sender.username,
|
||||||
|
avatar: msg.sender.avatar || undefined,
|
||||||
|
nickname: msg.sender.nickname,
|
||||||
|
} : undefined,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,21 +9,20 @@ export interface UserDTO {
|
|||||||
id: string;
|
id: string;
|
||||||
username: string;
|
username: string;
|
||||||
nickname: string;
|
nickname: string;
|
||||||
avatar: string | null;
|
avatar: string;
|
||||||
cover_url: string | null; // 头图URL
|
cover_url: string;
|
||||||
bio: string | null;
|
bio: string;
|
||||||
website: string | null;
|
website: string;
|
||||||
location: string | null;
|
location: string;
|
||||||
phone: string | null;
|
phone?: string;
|
||||||
email: string | null;
|
email?: string;
|
||||||
email_verified?: boolean;
|
email_verified?: boolean;
|
||||||
posts_count: number;
|
posts_count: number;
|
||||||
followers_count: number;
|
followers_count: number;
|
||||||
following_count: number;
|
following_count: number;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
// 额外字段
|
is_following?: boolean;
|
||||||
is_following?: boolean; // 当前用户是否关注了该用户
|
is_following_me?: boolean;
|
||||||
is_following_me?: boolean; // 该用户是否关注了当前用户
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserDetailDTO {
|
export interface UserDetailDTO {
|
||||||
@@ -246,6 +245,8 @@ export interface ConversationResponse {
|
|||||||
participants?: UserDTO[]; // 私聊时使用
|
participants?: UserDTO[]; // 私聊时使用
|
||||||
member_count?: number; // 群聊时使用
|
member_count?: number; // 群聊时使用
|
||||||
group?: GroupResponse; // 群聊会话的群组信息
|
group?: GroupResponse; // 群聊会话的群组信息
|
||||||
|
my_last_read_seq?: number; // 当前用户的已读位置
|
||||||
|
other_last_read_seq?: number; // 对方用户的已读位置
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
@@ -269,6 +270,7 @@ export interface ConversationDetailResponse {
|
|||||||
participants: UserDTO[];
|
participants: UserDTO[];
|
||||||
my_last_read_seq: number; // 当前用户的已读位置
|
my_last_read_seq: number; // 当前用户的已读位置
|
||||||
other_last_read_seq: number; // 对方用户的已读位置
|
other_last_read_seq: number; // 对方用户的已读位置
|
||||||
|
group?: GroupResponse; // 群聊会话的群组信息
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user