新增消息列表API,优化消息列表组件以支持加载状态和数据同步,提升用户体验和代码可读性。

This commit is contained in:
2025-10-23 17:02:33 +08:00
parent 75e62cce72
commit 7f4bc0487e
9 changed files with 1343 additions and 104 deletions

View File

@@ -4,25 +4,15 @@
* 架构设计:
* 1. 使用serverId作为数据库主键直接对应接口返回的id字段
* 2. 保留原始的id字段用于存储接口数据的完整性
* 3. 简化数据处理逻辑避免ID映射的复杂性
* 3. 添加userId字段实现多用户数据隔离
* 4. 统一会话表和联系人表,兼容好友和群聊
*
* 优势:
* - 直接使用服务器ID作为主键避免ID冲突
* - 保持数据的一致性和可追溯性
* - 简化查询逻辑,提高性能
* - 支持重复数据检测和去重
*
* 使用方法:
* - 存储接口数据:使用 createWithServerId() 或 createManyWithServerId()
* - 查询数据:使用 findById(id) 根据原始ID查询或 findByPrimaryKey(serverId) 根据主键查询
* - 批量查询:使用 findByIds([id1, id2, ...]) 根据原始ID批量查询
* - 内部操作serverId作为主键用于数据库内部管理
*
* 示例:
* const serverData = { id: 1001, name: '测试', ... }; // 接口返回的数据
* const serverId = await service.createWithServerId(serverData); // 存储返回serverId
* const data = await service.findById(1001); // 根据原始ID查询用户友好
* const dataByPK = await service.findByPrimaryKey(serverId); // 根据主键查询(内部使用)
* - 通过userId实现多用户数据隔离
* - 统一的会话和联系人表结构,兼容好友和群聊
* - 支持复合索引,提高查询性能
* - 支持用户登录记录和自动清理
*/
import Dexie, { Table } from "dexie";
@@ -33,33 +23,136 @@ import {
MessageListData,
} from "@/pages/pc/ckbox/data";
// 数据类型定义使用serverId作为主键
// ==================== 用户登录记录 ====================
export interface UserLoginRecord {
serverId: string; // 主键: user_${userId}
userId: number; // 用户ID
lastLoginTime: string; // 最后登录时间
loginCount: number; // 登录次数
createTime: string; // 首次登录时间
lastActiveTime: string; // 最后活跃时间
}
// ==================== 统一会话表(兼容好友和群聊) ====================
export interface ChatSession {
serverId: string; // 主键
userId: number; // 用户ID数据隔离
id: number; // 原始ID
type: "friend" | "group"; // 类型:好友或群聊
// 通用字段
wechatAccountId: number; // 所属客服账号
nickname: string; // 显示名称
conRemark?: string; // 备注名
avatar: string; // 头像
content: string; // 最新消息内容
lastUpdateTime: string; // 最后更新时间
config: {
unreadCount: number; // 未读数量
top: boolean; // 是否置顶
};
sortKey: string; // 预计算排序键
// 好友特有字段type='friend'时有效)
wechatFriendId?: number; // 好友ID
wechatId?: string; // 微信号
alias?: string; // 别名
// 群聊特有字段type='group'时有效)
chatroomId?: string; // 群聊ID
chatroomOwner?: string; // 群主
selfDisplayName?: string; // 群内昵称
notice?: string; // 群公告
}
// ==================== 统一联系人表(兼容好友和群聊) ====================
export interface Contact {
serverId: string; // 主键
userId: number; // 用户ID数据隔离
id: number; // 原始ID
type: "friend" | "group"; // 类型:好友或群聊
// 通用字段
wechatAccountId: number; // 所属客服账号
nickname: string; // 显示名称
conRemark?: string; // 备注名
avatar: string; // 头像
lastUpdateTime: string; // 最后更新时间
config?: any; // 配置信息
sortKey: string; // 预计算排序键
searchKey: string; // 预计算搜索键
// 好友特有字段type='friend'时有效)
wechatFriendId?: number; // 好友ID
wechatId?: string; // 微信号
alias?: string; // 别名
gender?: number; // 性别
region?: string; // 地区
signature?: string; // 个性签名
phone?: string; // 手机号
quanPin?: string; // 全拼
// 群聊特有字段type='group'时有效)
chatroomId?: string; // 群聊ID
chatroomOwner?: string; // 群主
selfDisplayName?: string; // 群内昵称
notice?: string; // 群公告
memberCount?: number; // 群成员数量
}
// ==================== 联系人标签映射表 ====================
export interface ContactLabelMap {
serverId: string; // 主键: ${contactId}_${labelId}
userId: number; // 用户ID数据隔离
labelId: number; // 标签ID
contactId: number; // 联系人ID
contactType: "friend" | "group"; // 联系人类型
sortKey: string; // 预计算排序键
searchKey: string; // 预计算搜索键
// 列表展示必需字段(轻量)
avatar: string;
nickname: string;
conRemark?: string;
unreadCount: number;
lastUpdateTime: string;
}
// ==================== 保留原有数据类型(向后兼容) ====================
export interface KfUserWithServerId extends Omit<KfUserListData, "id"> {
serverId: number | string; // 服务器ID作为主键
userId: number; // 用户ID数据隔离
id?: number; // 接口数据的原始ID字段
}
// 新联系人列表数据接口
export interface NewContactListData {
serverId: number | string; // 服务器ID作为主键
userId: number; // 用户ID数据隔离
id?: number; // 接口数据的原始ID字段
groupName: string;
contacts: ContractData[];
weChatGroup: weChatGroup[];
contacts: Contact[];
weChatGroup: Contact[];
}
// 数据库类
class CunkebaoDatabase extends Dexie {
// ==================== 保留原有表(向后兼容) ====================
kfUsers!: Table<KfUserWithServerId>;
weChatGroup!: Table<weChatGroup>;
contracts!: Table<ContractData>;
newContractList!: Table<NewContactListData>;
newContactList!: Table<NewContactListData>;
messageList!: Table<MessageListData>;
// ==================== 新增统一表 ====================
chatSessions!: Table<ChatSession>; // 统一会话表
contactsUnified!: Table<Contact>; // 统一联系人表
contactLabelMap!: Table<ContactLabelMap>; // 联系人标签映射表
userLoginRecords!: Table<UserLoginRecord>; // 用户登录记录表
constructor() {
super("CunkebaoDatabase");
// 版本1使用serverId作为主键的架构
// 版本1使用serverId作为主键的架构(保留原有表)
this.version(1).stores({
kfUsers:
"serverId, id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline",
@@ -67,10 +160,64 @@ class CunkebaoDatabase extends Dexie {
"serverId, id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar,wechatChatroomId, groupId, config, notice, selfDisplyName",
contracts:
"serverId, id, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, duplicate",
newContractList: "serverId, id, groupName, contacts",
newContactList: "serverId, id, groupName, contacts",
messageList:
"serverId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName",
});
// 版本2添加用户隔离字段到原有表
this.version(2)
.stores({
kfUsers:
"serverId, userId, id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline",
weChatGroup:
"serverId, userId, id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar,wechatChatroomId, groupId, config, notice, selfDisplyName",
contracts:
"serverId, userId, id, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, duplicate",
newContactList: "serverId, userId, id, groupName, contacts",
messageList:
"serverId, userId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName",
})
.upgrade(trans => {
// 为现有数据添加默认userId=0需要手动清理
return trans
.table("kfUsers")
.toCollection()
.modify(item => {
item.userId = 0;
});
});
// 版本3添加新的统一表和用户登录记录表
this.version(3).stores({
// 保留原有表
kfUsers:
"serverId, userId, id, tenantId, wechatId, nickname, alias, avatar, gender, region, signature, bindQQ, bindEmail, bindMobile, createTime, currentDeviceId, isDeleted, deleteTime, groupId, memo, wechatVersion, lastUpdateTime, isOnline",
weChatGroup:
"serverId, userId, id, wechatAccountId, tenantId, accountId, chatroomId, chatroomOwner, conRemark, nickname, chatroomAvatar,wechatChatroomId, groupId, config, notice, selfDisplyName",
contracts:
"serverId, userId, id, wechatAccountId, wechatId, alias, conRemark, nickname, quanPin, avatar, gender, region, addFrom, phone, signature, accountId, extendFields, city, lastUpdateTime, isPassed, tenantId, groupId, thirdParty, additionalPicture, desc, config, lastMessageTime, duplicate",
newContactList: "serverId, userId, id, groupName, contacts",
messageList:
"serverId, userId, id, dataType, wechatAccountId, tenantId, accountId, nickname, avatar, groupId, config, labels, wechatId, alias, conRemark, quanPin, gender, region, addFrom, phone, signature, extendFields, city, lastUpdateTime, isPassed, thirdParty, additionalPicture, desc, lastMessageTime, duplicate, chatroomId, chatroomOwner, chatroomAvatar, notice, selfDisplyName",
// 新增统一表
// 会话表索引:支持按用户、类型、时间、置顶等查询
chatSessions:
"serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], [userId+lastUpdateTime], sortKey, nickname, conRemark, avatar, content, lastUpdateTime",
// 联系人表索引:支持按用户、类型、标签、搜索等查询
contactsUnified:
"serverId, userId, id, type, wechatAccountId, [userId+type], [userId+wechatAccountId], sortKey, searchKey, nickname, conRemark, avatar, lastUpdateTime",
// 联系人标签映射表索引:支持按用户、标签、联系人、类型查询
contactLabelMap:
"serverId, userId, labelId, contactId, contactType, [userId+labelId], [userId+contactId], [userId+labelId+sortKey], sortKey, searchKey, avatar, nickname, conRemark, unreadCount, lastUpdateTime",
// 用户登录记录表索引支持按用户ID、登录时间查询
userLoginRecords:
"serverId, userId, lastLoginTime, loginCount, createTime, lastActiveTime",
});
}
}
@@ -335,8 +482,14 @@ export class DatabaseService<T> {
export const kfUserService = new DatabaseService(db.kfUsers);
export const weChatGroupService = new DatabaseService(db.weChatGroup);
export const contractService = new DatabaseService(db.contracts);
export const newContactListService = new DatabaseService(db.newContractList);
export const newContactListService = new DatabaseService(db.newContactList);
export const messageListService = new DatabaseService(db.messageList);
// 新增统一表服务
export const chatSessionService = new DatabaseService(db.chatSessions);
export const contactUnifiedService = new DatabaseService(db.contactsUnified);
export const contactLabelMapService = new DatabaseService(db.contactLabelMap);
export const userLoginRecordService = new DatabaseService(db.userLoginRecords);
// 默认导出数据库实例
export default db;